Project

General

Profile

Download (27.4 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 org.eclipse.core.runtime.Assert;
16
import org.eclipse.jface.dialogs.Dialog;
17
import org.eclipse.jface.text.IDocument;
18
import org.eclipse.jface.text.Position;
19
import org.eclipse.jface.window.DefaultToolTip;
20
import org.eclipse.swt.custom.StyledText;
21
import org.eclipse.swt.dnd.DND;
22
import org.eclipse.swt.dnd.DragSource;
23
import org.eclipse.swt.dnd.Transfer;
24
import org.eclipse.swt.events.ControlAdapter;
25
import org.eclipse.swt.events.ControlEvent;
26
import org.eclipse.swt.events.ControlListener;
27
import org.eclipse.swt.events.FocusAdapter;
28
import org.eclipse.swt.events.FocusEvent;
29
import org.eclipse.swt.events.FocusListener;
30
import org.eclipse.swt.events.ModifyEvent;
31
import org.eclipse.swt.events.ModifyListener;
32
import org.eclipse.swt.events.MouseAdapter;
33
import org.eclipse.swt.events.MouseEvent;
34
import org.eclipse.swt.graphics.Color;
35
import org.eclipse.swt.graphics.Font;
36
import org.eclipse.swt.graphics.Image;
37
import org.eclipse.swt.widgets.Composite;
38
import org.eclipse.swt.widgets.Control;
39
import org.eclipse.swt.widgets.Display;
40
import org.eclipse.swt.widgets.Label;
41
import org.eclipse.swt.widgets.Menu;
42
import org.eclipse.ui.forms.IFormPart;
43
import org.eclipse.ui.forms.IManagedForm;
44
import org.eclipse.ui.forms.widgets.TableWrapData;
45
import org.eclipse.ui.forms.widgets.TableWrapLayout;
46

    
47
import eu.etaxonomy.cdm.common.CdmUtils;
48
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
49
import eu.etaxonomy.cdm.model.name.NameRelationship;
50
import eu.etaxonomy.cdm.model.name.NonViralName;
51
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
52
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
53
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
54
import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
55
import eu.etaxonomy.taxeditor.editor.EditorUtil;
56
import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
57
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation.EditorAnnotationType;
58
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragListener;
59
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragSourceEffect;
60
import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
61
import eu.etaxonomy.taxeditor.labels.ILabelImageStrategy;
62
import eu.etaxonomy.taxeditor.labels.LabelImageProvider;
63
import eu.etaxonomy.taxeditor.model.IElementHasDetails;
64
import eu.etaxonomy.taxeditor.model.NameHelper;
65
import eu.etaxonomy.taxeditor.model.TextHelper;
66
import eu.etaxonomy.taxeditor.parser.ParseHandler;
67
import eu.etaxonomy.taxeditor.preference.Resources;
68
import eu.etaxonomy.taxeditor.store.singlesource.widget.DisplayProxy;
69

    
70
/**
71
 * Formats <code>GroupedComposite</code> with cosmetic and layout properties specific to the
72
 * Editor. This should be used to maintain a consistent look and feel for all Editor
73
 * freetext area components, such as DescriptionElementComposite.
74
 * <p>
75
 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the contents
76
 * of {@link #getData()} when the <code>GroupedComposite</code> gets focus, i.e. to
77
 * populate the property sheet with the data.
78
 * </p>
79
 * <p>
80
 * The <code>IManagedForm</code> is also required to have a <code>Taxon</code> in its
81
 * own <code>getData()</code>.
82
 * </p>
83
 * <p>
84
 * The <code>IManagedForm</code> can also used for drawing borders by calling the method
85
 * <code>createBorderSupport()</code>.
86
 * </p>
87
 *
88
 * @author p.ciardelli
89
 * @author n.hoffmann
90
 * @created 02.06.2008
91
 * @version 1.0
92
 */
93
abstract public class AbstractGroupedContainer<T extends TaxonBase> implements IFormPart, IContainerConstants, IElementHasDetails {	
94

    
95
	protected ParseHandler parseHandler;
96
	
97
	private FocusListener nameCompositeFocusListener;
98
	private ModifyListener nameCompositeModifyListener;
99

    
100
	protected NameViewer nameViewer;
101
	
102
	private AbstractGroup group;
103
	
104
	private Label nonEditableInfoLabel;
105
	private DefaultToolTip nonEditableInfoHover;
106
	
107
	private static AbstractGroupedContainer selection;
108
	
109
	private FocusListener focusListener;
110
	private LineBreakListener lineBreakListener;
111

    
112
	private int cursorPosition;
113

    
114
	protected Composite control;
115

    
116
	private Color backgroundColor;
117
	private boolean isDirty;
118
	
119
	
120
	/**
121
	 * <p>Constructor for AbstractGroupedContainer.</p>
122
	 *
123
	 * @param editor a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor} object.
124
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
125
	 * @param taxonBase a T object.
126
	 * @param <T> a T object.
127
	 */
128
	public AbstractGroupedContainer(T taxonBase){
129
		setData(taxonBase);
130
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
131
	}
132
	
133
	public void createContent(){
134
		createControl();
135
		
136
		createTextViewer();
137
		createLineWrapSupport();
138
		createLineBreakListener();
139

    
140
		setMenu(getEditor().getMenu());
141
		
142
		setDraggableControl(new Control[]{getControl(), getNameViewer().getRulerControl()});
143
		
144
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
145
		
146
		
147
		initializeComposite();
148
		
149
		createListener();
150

    
151
		isFreeTextEnabled();
152
	}
153

    
154
	
155
	
156
	/**
157
	 * <p>createListener</p>
158
	 */
159
	protected void createListener(){
160
		nameCompositeModifyListener = new ModifyListener(){
161
			
162
			public void modifyText(ModifyEvent e) {
163
				// mark the composite dirty
164
				setDirty(true);
165
				// parse the text
166
				String text = nameViewer.getTextWidget().getText();
167
				
168
				NonViralName name = parseHandler.parse(text);
169
				getTaxonBase().setName(name);
170
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
171
				
172
				// show errors resulting from parsing
173
				calculateAnnotations();
174
				// store the position of the cursor				
175
				storeCursor();
176
				// notify selection listener
177
				setDelayedSelection();
178
			}
179
		};
180
		nameCompositeFocusListener = new FocusAdapter(){
181

    
182
			/* (non-Javadoc)
183
			 * @see org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt.events.FocusEvent)
184
			 */
185
			@Override
186
			public void focusLost(FocusEvent e) {
187
				super.focusLost(e);
188

    
189
				persistName();
190
			}		
191
		};
192
		
193
		addListener();
194
	}
195
	
196
	private void addListener(){
197
		getNameViewer().getTextWidget().addModifyListener(nameCompositeModifyListener);
198
		getNameViewer().getTextWidget().addFocusListener(nameCompositeFocusListener);
199
	}
200
	
201
	private void removeListener(){
202
		getNameViewer().getTextWidget().removeModifyListener(nameCompositeModifyListener);
203
		getNameViewer().getTextWidget().removeFocusListener(nameCompositeFocusListener);
204
	}
205
		
206
	/**
207
	 * Initialize the composite specific code
208
	 */
209
	protected abstract void initializeComposite();
210
	
211
	/**
212
	 * <p>getEmptyTextPrompt</p>
213
	 *
214
	 * @return a {@link java.lang.String} object.
215
	 */
216
	protected String getEmptyTextPrompt() {
217
		return EMPTY_NAME_PROMPT;
218
	}
219
	
220
	/**
221
	 * 
222
	 */
223
	private void showNameRelations() {
224
		TaxonNameBase<?, ?> name = getName();
225
		if (name == null) {
226
			return;
227
		}
228
		
229
		ILabelImageStrategy strategy = LabelImageProvider.getLabelStrategy(name);
230
		LabelImageProvider labelProvider = new LabelImageProvider(strategy);
231
		
232
		Set<NameRelationship> nameRelations = name.getNameRelations();
233
		if (nameRelations.size() == 0) {
234
			return;
235
		}
236
//		for (NameRelationship nameRelation : nameRelations) {
237
//			String typeLabel = null;
238
//			TaxonNameBase<?, ?> relatedName = null; 
239
//
240
//			if (name.equals(nameRelation.getFromName())) {
241
//				typeLabel = labelProvider.getNameRelationTypeLabel( 
242
//								nameRelation.getType());
243
//				relatedName = nameRelation.getToName();
244
//			} else {
245
//				typeLabel = labelProvider.getNameRelationTypeInverseLabel( 
246
//								nameRelation.getType());
247
//				relatedName = nameRelation.getFromName();
248
//			}
249
//			
250
//			setNonEditableInfo(typeLabel + " " + NameHelper.getDisplayName(relatedName));
251
//		}
252
	}
253
		
254
	/**
255
	 * <p>initTextViewer</p>
256
	 */
257
	protected void initTextViewer() {
258
		
259
//		showNameRelations();
260
		
261
		updateIndent();
262
		
263
		updateIcon();
264
		
265
		String text = NameHelper.getDisplayNameWithRef(getData());
266
		
267
		if (text.length() == 0) {
268
			initEmptyText();
269
		} else {
270
			getNameViewer().setText(text);
271
			placeCursor();
272
		}
273
		calculateAnnotations();
274
	}
275
	
276
	/**
277
	 * <p>calculateErrors</p>
278
	 */
279
	synchronized protected void calculateAnnotations() {
280
		getNameViewer().clearAnnotations();
281
		showAnnotations();
282
	}
283

    
284
	/**
285
	 * 
286
	 */
287
	public void showAnnotations() {
288
		
289
		if(getName().hasProblem()){
290
			showParsingProblems();
291
		}
292
		
293
		if(!isNameParsable()){
294
			getNameViewer().addAnnotation(
295
					new EditorAnnotation(EditorAnnotationType.WARNING, 0, "This name may only be edited in the details view."));
296
		}
297
		
298
		if(isNameUsedMultipleTimes()){
299
			getNameViewer().addAnnotation(new EditorAnnotation(EditorAnnotationType.WARNING, 0, "This taxons name is used multiple times."));
300
		}
301
		
302
		
303
	}
304

    
305
	/**
306
	 * 
307
	 */
308
	private void showParsingProblems() {
309
		String text = getNameViewer().getTextWidget().getText();
310
		
311
		List<ParserProblem> parsingProblems = getName().getParsingProblems();
312
		
313
		for (ParserProblem problem : parsingProblems) {
314
			getNameViewer().addAnnotation(new EditorAnnotation(problem), getParsingProblemPosition());
315
		}	
316
	}
317
	
318
	private Position getParsingProblemPosition(){
319
		String text = getNameViewer().getTextWidget().getText();
320
		
321
		if (getName().hasProblem() && text.length() > 0) {
322
			int start = getName().getProblemStarts();
323
			int length = getName().getProblemEnds() - start;
324
			
325
			if (start == -1 || getName().getProblemEnds() == -1) {
326
				return null;
327
			}
328
			
329
			// Don't let squigglies try to draw beyond the end of the text
330
			if (text.length() < start + length) {
331
				length = text.length() - start;
332
			}
333
			
334
			return new Position(start, length);
335
		}
336
		return null;
337
	}
338

    
339
	/**
340
	 * <p>handleSplitText</p>
341
	 *
342
	 * @param text a {@link java.lang.String} object.
343
	 */
344
	protected void handleSplitText(String text) {
345
		// Create a synonym in a new homotypic group using text as name
346
		TaxonNameBase synonymName = ParseHandler.parseReferencedName(text, null);
347
		
348
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation
349
				("New Heterotypic Synonym", getEditor().getUndoContext(), getEditor().getTaxon(), synonymName, getEditor()));
350
	}
351

    
352
	/**
353
	 * Refreshes the display with latest data from the model.
354
	 *
355
	 * Note: Will not parse the text and not calculate errors!
356
	 */
357
	public void refresh() {
358
//		showNameRelations();
359
		
360
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
361
		
362
		if(getNameViewer().getTextWidget() == null){
363
			// we might get here via dnd. Look slike it can be ignored
364
			return;
365
		}
366
		
367
		if (text.length() == 0) {
368
			initEmptyText();
369
		} else if(! getNameViewer().getTextWidget().getText().equals(text)) {
370
			removeListener();
371
			getNameViewer().getTextWidget().setText(text);
372
			addListener();
373
		}
374
		
375
		updateNonEditableInfo();
376
		
377
		updateIcon();
378
//		placeCursor();
379
		updateIndent();
380
		
381
		isFreeTextEnabled();
382
	}
383

    
384
	/**
385
	 * 
386
	 */
387
	protected abstract void updateIcon();
388

    
389
	
390
	protected abstract void updateIndent();
391

    
392

    
393
	/**
394
	 * <p>updateNonEditableInfo</p>
395
	 */
396
	protected abstract void updateNonEditableInfo();
397
	
398
	/**
399
	 * 
400
	 */
401
	private void isFreeTextEnabled() {
402
		// Decide whether editing of freetext is allowed or not.
403
		if (freetextEditingAllowed()) {
404
			
405
			// set editable
406
			getNameViewer().getTextWidget().setEditable(true);
407
			
408
			// restore foreground font color again
409
			getNameViewer().getTextWidget().setForeground(control.getForeground());
410
		} else {
411
			// set non-editable
412
			getNameViewer().getTextWidget().setEditable(false);
413
			
414
			// grey out text as a non-editable indicator
415
			getNameViewer().getTextWidget().setForeground(EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR));
416
		}
417
	}
418
	
419

    
420
	/**
421
	 * Checks whether the freetext should be editable based on specific empty fields.
422
	 * @return
423
	 */
424
	private boolean freetextEditingAllowed() {
425
		NonViralName name = (NonViralName) HibernateProxyHelper.deproxy(getName());
426
		boolean enableFreetext = true;
427

    
428
		enableFreetext |= isNameUsedMultipleTimes();
429
		enableFreetext &= isNameParsable();
430
		
431
		return enableFreetext;
432
	}
433
	
434
	private boolean isNameUsedMultipleTimes(){
435
		if(getName().getTaxonBases().size() > 1){
436
			return true;
437
		}
438
		return false;
439
	}
440
	
441
	private boolean isNameParsable(){
442
		TaxonNameBase name = getName();
443
		
444
		boolean isParsable = true; 
445
		isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); //taxonFieldsEmpty();
446
		
447
		if(name instanceof NonViralName){
448
			NonViralName nonViralName = (NonViralName) name;
449
			isParsable &= ! nonViralName.isProtectedAuthorshipCache();
450
			isParsable &= ! nonViralName.isProtectedNameCache();
451
		}
452
		
453
		return isParsable;
454
	}
455

    
456
	/**
457
	 * Parse the text and calculate errors
458
	 */
459
	public void parseAndCalculateAnnotations(){
460
		removeListener();
461
		String unparsedNameString = getNameViewer().getTextWidget().getText();
462
		parseHandler.parse(unparsedNameString);
463
		addListener();
464
		calculateAnnotations();
465
	}
466

    
467
	/**
468
	 * <p>getTaxonBase</p>
469
	 *
470
	 * @return the taxonBase
471
	 */
472
	public T getTaxonBase() {
473
		return getData();
474
	}
475
	
476
	/**
477
	 * <p>getName</p>
478
	 *
479
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
480
	 */
481
	public TaxonNameBase getName(){
482
		return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase().getName());
483
	}
484

    
485
	/**
486
	 * <p>persistName</p>
487
	 */
488
	public void persistName(){
489
		if(isDirty()){
490
			getNameViewer().getTextWidget().setEnabled(false);
491
			final String unparsedNameString = getNameViewer().getTextWidget().getText();
492
//			Job job = new Job("Persisting Name"){
493
//
494
//				@Override
495
//				protected IStatus run(IProgressMonitor monitor) {
496
//					
497
					final NonViralName name = parseHandler.parseAndResolveDuplicates(unparsedNameString);
498
//					
499
//					Display.getDefault().asyncExec(new Runnable(){
500
//						public void run() {
501
							getTaxonBase().setName(name);
502
							getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
503
							setDirty(false);
504
							getNameViewer().getTextWidget().setEnabled(true);							
505
//						};
506
//					});
507
//					
508
//					
509
//					return Status.OK_STATUS;
510
//				}
511
//				
512
//			};
513
//			
514
//			job.setPriority(Job.DECORATE);
515
//			job.schedule();
516
		}
517
	}
518
	
519
	/**
520
	 * <p>Getter for the field <code>group</code>.</p>
521
	 *
522
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
523
	 */
524
	public AbstractGroup getGroup(){
525
		if(group == null){
526
			throw new IllegalStateException("Group shall not be null.");
527
		}
528
		return group;
529
	}
530
	
531
	/**
532
	 * <p>remove</p>
533
	 */
534
	public void remove(){
535
		getGroup().remove(this);
536
	}
537
		
538
	/**
539
	 * <p>createControl</p>
540
	 */
541
	protected void createControl() {
542
		control = getEditor().getToolkit().createComposite(getGroup().getControl());
543
		
544
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
545
		TableWrapLayout layout = new TableWrapLayout();
546
		layout.leftMargin = 0;
547
		layout.rightMargin = 0;
548
		layout.topMargin = 5;
549
		layout.bottomMargin = 5;
550
		
551
		layout.verticalSpacing = 0;
552
		layout.horizontalSpacing = 0;
553
		
554
		control.setLayout(layout);
555
		
556
	}
557
	
558
	/**
559
	 * @return
560
	 */
561
	protected TaxonNameEditor getEditor() {
562
		return getGroup().getEditor();
563
	}
564

    
565
	/**
566
	 * <p>Getter for the field <code>control</code>.</p>
567
	 *
568
	 * @return a {@link org.eclipse.swt.widgets.Composite} object.
569
	 */
570
	public Composite getControl(){
571
		return control;
572
	}
573
	
574
	/**
575
	 * <p>createLineWrapSupport</p>
576
	 */
577
	protected void createLineWrapSupport() {
578
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
579
	}
580
	
581
	
582
	/**
583
	 * <p>createTextViewer</p>
584
	 */
585
	protected void createTextViewer() {
586
		nameViewer = new NameViewer(control);
587
		
588
		focusListener = new FocusAdapter() {
589
			@Override
590
			public void focusGained(FocusEvent e) {
591
				for(AbstractGroupedContainer container : getEditor().getGroupedContainers()){
592
					container.colorSelected(NOT_SELECTED);
593
				}
594
				getEditor().getManagedForm().setInput(AbstractGroupedContainer.this);
595
				placeCursor();
596
				colorSelected(SELECTED_FOCUS);
597
			}
598
		};
599
		nameViewer.getTextWidget().addFocusListener(focusListener);
600
		
601
//		
602
		MouseAdapter mouseListener = new MouseAdapter() {
603
			@Override
604
			public void mouseDown(MouseEvent e) {
605
				storeCursor();
606
			}
607
		};
608
		control.addMouseListener(mouseListener);
609
		nameViewer.getRulerControl().addMouseListener(mouseListener);
610
		nameViewer.getTextWidget().addMouseListener(mouseListener);
611
	}
612
	
613
	/**
614
	 * <p>setIcon</p>
615
	 *
616
	 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
617
	 */
618
	public void setIcon(Image icon) {
619
		getNameViewer().setIcon(icon);
620
	}
621
	
622
	/**
623
	 * <p>setIndent</p>
624
	 *
625
	 * @param indent a int.
626
	 */
627
	public void setIndent(int indent) {
628
		if (control.getLayout() instanceof TableWrapLayout) {
629
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
630
			layout.leftMargin = indent;
631
			layout.rightMargin = ACCEPTED_INDENT;
632
			control.setLayout(layout);
633
			control.layout();
634
		} else {
635
			new RuntimeException("Couldn't indent - composite's layout must be TableWrapLayout.");
636
		}
637
	}	
638
	
639
	/**
640
	 * <p>setSelected</p>
641
	 */
642
	public void setSelected() {
643
		getNameViewer().getTextWidget().setFocus();
644
	}
645
	
646
	/**
647
	 * <p>isSelected</p>
648
	 *
649
	 * @return a boolean.
650
	 */
651
	public boolean isSelected(){
652
		return getEditor().getSelectedContainer() == this;
653
	}
654
	
655
	/**
656
	 * <p>colorSelected</p>
657
	 *
658
	 * @param mode a int.
659
	 */
660
	public void colorSelected(int mode){
661
		if(!control.isDisposed()){
662
			String colorString = null;
663
			
664
			switch(mode){
665
			case SELECTED_FOCUS:
666
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
667
				break;
668
			case SELECTED_NO_FOCUS:
669
				colorString = Resources.COLOR_CONTROL_SELECTED;
670
				break;
671
			default:
672
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
673
			}
674
			
675
			backgroundColor = EditorUtil.getColor(colorString);
676
			
677
			setBackground(backgroundColor);
678
		}
679
	}
680

    
681
	
682
	/**
683
	 * <p>setDelayedSelection</p>
684
	 */
685
	protected void setDelayedSelection(){
686
		//TODO this might be done better
687
		// this is the quickest solution i could come up with and it improves performance
688
		// please reimplement if you know better.
689
		selection = this;
690
		
691
		// start timer
692
		Display display = DisplayProxy.getDefault();
693
		Runnable runnable = new Runnable() {
694
			
695
			public void run() {
696
				getEditor().getManagedForm().setInput(selection);
697
			}
698
		};
699
		display.timerExec(1000, runnable);
700
		
701
	}
702
	
703
	/**
704
	 * <p>setBackground</p>
705
	 *
706
	 * @param color a {@link org.eclipse.swt.graphics.Color} object.
707
	 */
708
	public void setBackground(Color color) {
709
		control.setBackground(color);
710
		
711
		for(Control child : control.getChildren()){
712
			child.setBackground(color);
713
		}
714
		
715
		getNameViewer().setBackground(color);
716
	}
717

    
718
	/* (non-Javadoc)
719
	 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
720
	 */
721
	/**
722
	 * <p>setFont</p>
723
	 *
724
	 * @param font a {@link org.eclipse.swt.graphics.Font} object.
725
	 */
726
	public void setFont(Font font) {
727
		getNameViewer().getTextWidget().setFont(font);
728
	}
729
	
730
	/**
731
	 * <p>Getter for the field <code>nameViewer</code>.</p>
732
	 *
733
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer} object.
734
	 */
735
	public NameViewer getNameViewer() {
736
		if (nameViewer == null){
737
			throw new RuntimeException("The Name Viewer is corrupt for Name Container: " + getTaxonBase().getName().getTitleCache());
738
		}
739
		return nameViewer;
740
	}
741
	
742
	/**
743
	 * If <code>textViewer</code> has already been set, it will show a
744
	 * <code>prompt</code> along the lines of "Click here to start entering data"
745
	 * when empty.
746
	 *
747
	 * @param prompt a {@link java.lang.String} object.
748
	 */
749
	public void createEmptyViewerPrompt(final String prompt) {
750
		
751
		Assert.isNotNull(getNameViewer());
752
		 
753
		final StyledText textControl = getNameViewer().getTextWidget();
754
		final IDocument document = getNameViewer().getDocument();
755
		
756
		setFocusListener(new FocusListener() {
757

    
758
			
759
			public void focusGained(FocusEvent e) {
760
				if (document.get().equals(prompt)) {
761
					textControl.setFont(getViewerFont());
762
					document.set("");
763
				}
764
			}
765

    
766
			
767
			public void focusLost(FocusEvent e) {
768
				if (document.getLength() == 0) {
769
					initEmptyText();
770
				}
771
			}
772
			
773
		});
774
		textControl.addFocusListener(getFocusListener());
775
		
776
		if (document.getLength() == 0) {
777
			textControl.setFont(EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT));
778
			document.set(prompt);
779
		}
780
	}
781
	
782
	/**
783
	 * <p>getViewerFont</p>
784
	 *
785
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
786
	 */
787
	abstract protected Font getViewerFont();
788

    
789
	/**
790
	 * <p>initEmptyText</p>
791
	 */
792
	protected void initEmptyText() {
793
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
794
		getNameViewer().getTextWidget().setFont(defaultFont);
795
		
796
		getNameViewer().getDocument().set(getEmptyTextPrompt());
797
		placeCursor();
798
	}
799

    
800
	/**
801
	 * <p>Setter for the field <code>focusListener</code>.</p>
802
	 *
803
	 * @param focusListener a {@link org.eclipse.swt.events.FocusListener} object.
804
	 */
805
	protected void setFocusListener(FocusListener focusListener) {
806
		this.focusListener = focusListener;
807
	}
808

    
809
	private FocusListener getFocusListener() {
810
		return focusListener;
811
	}
812

    
813
	/**
814
	 * <p>setDirty</p>
815
	 *
816
	 * @param isDirty a boolean.
817
	 */
818
	public void setDirty(boolean isDirty) {
819
		if(isDirty){
820
			getEditor().getManagedForm().dirtyStateChanged();
821
		}
822
		this.isDirty = isDirty;
823
	}
824
	
825
	/**
826
	 * <p>isDirty</p>
827
	 *
828
	 * @return a boolean.
829
	 */
830
	public boolean isDirty(){
831
		return isDirty;
832
	}
833
	
834
	/**
835
	 * <p>setMenu</p>
836
	 *
837
	 * @param menu a {@link org.eclipse.swt.widgets.Menu} object.
838
	 */
839
	public void setMenu (Menu menu) {
840
		control.setMenu(menu);
841
		
842
		getNameViewer().setMenu(menu);
843
	}
844
	
845
	private Control[] draggableControls;
846
	
847
	/**
848
	 * <p>setDraggableControl</p>
849
	 *
850
	 * @param controls an array of {@link org.eclipse.swt.widgets.Control} objects.
851
	 */
852
	protected void setDraggableControl(Control[] controls) {
853
		draggableControls = controls;
854
	}
855
	
856
	/**
857
	 * <p>setIsDraggable</p>
858
	 *
859
	 * @param draggable a boolean.
860
	 */
861
	public void setIsDraggable(boolean draggable) {
862

    
863
		if (draggable) {
864

    
865
			if (draggableControls == null) {
866
				throw new NullPointerException(
867
						"Draggable controls must be set to add draggability");
868
			}
869
			
870
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };			
871
			int operations = DND.DROP_MOVE;
872

    
873
			for(Control draggableControl : draggableControls){
874
				DragSource dragSource = new DragSource(draggableControl, operations);
875
				dragSource.setTransfer(types);
876
				
877
				dragSource.addDragListener(new NameEditorDragListener(this));
878
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(control));
879
			}
880
		} 
881
	}
882

    
883
	private String nonEditableText;
884

    
885
	ControlListener nonEditableResizeListener = new ControlAdapter() {
886
		
887
		int width = 0;
888
		
889
		@Override
890
		public void controlResized(ControlEvent e) {
891
			if (nonEditableInfoLabel.getBounds().width == width) {
892
				return;
893
			}
894
			width = nonEditableInfoLabel.getBounds().width;
895
			if (nonEditableInfoLabel.getBounds().width > 0) {
896
				nonEditableInfoLabel.setText(
897
						Dialog.shortenText(nonEditableText.toUpperCase(), 
898
						nonEditableInfoLabel));
899
			}
900
		}
901
	};
902

    
903
	private String nonEditableHoverText;
904

    
905
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
906

    
907
	private T data;
908
			
909
	/**
910
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
911
	 * input field. For instance, NameComposites display things like name relations,
912
	 * sec. references, etc. here.
913
	 *
914
	 * @param info the text to display in the label
915
	 * @param append whether the string should be appended to text that is already shown in the label
916
	 */
917
	public void setNonEditableInfo(String info, boolean append) {
918
		// TODO non editable info should only be drawn once, when everything else is drawn
919
		info = info.toUpperCase();
920
		
921
		if(append){
922
			nonEditableText += ", " + info;
923
			nonEditableHoverText += "\n" + info;
924
		}else{
925
			nonEditableText = info;
926
			nonEditableHoverText = info;
927
		}
928
		
929
		if (nonEditableInfoLabel == null) {
930
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(control, "");
931
			TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP);
932
			// Set indent to viewer ruler's width 
933
			if (getNameViewer().getRulerControl() != null) {
934
				// TODO right justify
935
				layoutData.indent = NameViewer.RULER_WIDTH;
936
			}
937
			nonEditableInfoLabel.setLayoutData(layoutData);
938
			
939

    
940
			
941
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(nonEditableInfoLabel) {
942
				@Override
943
				public String getLabelText() {
944
					return nonEditableText.toUpperCase();
945
				}
946
			}; 
947
			nonEditableInfoLabel.addControlListener(nonEditableLabelEllipsisListener);
948
			
949
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
950
			nonEditableInfoHover.setRespectDisplayBounds(true);
951
			
952
		} 
953
		nonEditableInfoHover.setText(nonEditableHoverText);
954
		nonEditableInfoLabel.setText(nonEditableText);
955
		
956
		calculateAnnotations();
957
	}
958
	
959
	/**
960
	 * <p>Getter for the field <code>data</code>.</p>
961
	 *
962
	 * @return a T object.
963
	 */
964
	public T getData(){
965
		return data;
966
	}
967
	
968
	/**
969
	 * <p>Setter for the field <code>data</code>.</p>
970
	 *
971
	 * @param data a T object.
972
	 */
973
	public void setData(T data){
974
		this.data =  (T) HibernateProxyHelper.deproxy(data);
975
	}
976

    
977
	/**
978
	 * If the user hitting carriage return should cause something to happen -
979
	 * i.e. the creation of a new composite - call this method and override
980
	 * the method handleSplitText().
981
	 */
982
	protected void createLineBreakListener() {
983
		lineBreakListener = new LineBreakListener() {
984
			@Override
985
			public void handleSplitText(String text) {
986
				AbstractGroupedContainer.this.handleSplitText(text);	
987
			}
988
		};
989
		
990
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
991
	}
992
		
993
	abstract class LabelEllipsisListener extends ControlAdapter {
994
		
995
		private final Label label;
996
		int width = 0;
997

    
998
		LabelEllipsisListener(Label label) {
999
			this.label = label;
1000
		}
1001
		
1002
		abstract public String getLabelText();
1003
		
1004
		@Override
1005
		public void controlResized(ControlEvent e) {
1006
			if (label.getBounds().width == width) {
1007
				return;
1008
			}
1009
			width = label.getBounds().width;
1010
			if (label.getBounds().width > 0) {
1011
				label.setText(TextHelper.shortenText(getLabelText(), label));
1012
			}
1013
		}
1014
	}
1015

    
1016
	/**
1017
	 * <p>storeCursor</p>
1018
	 */
1019
	public void storeCursor() {
1020
		this.cursorPosition = getNameViewer().getCursorPosition();
1021
	}
1022
	
1023
	/**
1024
	 * Puts the cursor to the position it was last seen on or to the end of line
1025
	 * if no former position is known.
1026
	 */
1027
	public void placeCursor(){
1028
		if(cursorPosition == 0){
1029
			getNameViewer().setCursorToEOL();
1030
		}else{
1031
			getNameViewer().setCursorPosition(cursorPosition);
1032
		}
1033
	}
1034

    
1035

    
1036

    
1037
	/**
1038
	 * <p>Setter for the field <code>group</code>.</p>
1039
	 *
1040
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
1041
	 */
1042
	public void setGroup(AbstractGroup group) {
1043
		this.group = group;
1044
	}
1045

    
1046

    
1047

    
1048
	/**
1049
	 * <p>restoreColor</p>
1050
	 */
1051
	public void restoreColor() {
1052
		setBackground(backgroundColor);
1053
	}
1054
	
1055

    
1056

    
1057
	/* (non-Javadoc)
1058
	 * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
1059
	 */
1060
	@Override
1061
	public void initialize(IManagedForm form) {
1062
		// TODO Auto-generated method stub
1063
		
1064
	}
1065

    
1066

    
1067
	/* (non-Javadoc)
1068
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1069
	 */
1070
	@Override
1071
	public void dispose() {
1072
		if(getControl() != null){
1073
			setMenu(null);
1074
			getControl().dispose();
1075
		}
1076
	}
1077

    
1078

    
1079
	/* (non-Javadoc)
1080
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1081
	 */
1082
	@Override
1083
	public void commit(boolean onSave) {
1084
		if(isDirty()){
1085
			persistName();
1086
		}
1087
	}
1088

    
1089

    
1090
	/* (non-Javadoc)
1091
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1092
	 */
1093
	@Override
1094
	public boolean setFormInput(Object input) {
1095
		// TODO Auto-generated method stub
1096
		return false;
1097
	}
1098

    
1099

    
1100
	/* (non-Javadoc)
1101
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1102
	 */
1103
	@Override
1104
	public void setFocus() {
1105
		getNameViewer().getControl().setFocus();
1106
	}
1107

    
1108

    
1109
	/* (non-Javadoc)
1110
	 * @see org.eclipse.ui.forms.IFormPart#isStale()
1111
	 */
1112
	@Override
1113
	public boolean isStale() {
1114
		// TODO Auto-generated method stub
1115
		return false;
1116
	}
1117
} 
(2-2/19)