Project

General

Profile

Download (28.1 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

    
69
/**
70
 * Formats <code>GroupedComposite</code> with cosmetic and layout properties
71
 * specific to the Editor. This should be used to maintain a consistent look and
72
 * feel for all Editor freetext area components, such as
73
 * DescriptionElementComposite.
74
 * <p>
75
 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the
76
 * contents of {@link #getData()} when the <code>GroupedComposite</code> gets
77
 * focus, i.e. to 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>
81
 * in its own <code>getData()</code>.
82
 * </p>
83
 * <p>
84
 * The <code>IManagedForm</code> can also used for drawing borders by calling
85
 * the method <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
94
		IFormPart, IContainerConstants, IElementHasDetails {
95

    
96
	protected ParseHandler parseHandler;
97

    
98
	private FocusListener nameCompositeFocusListener;
99
	private ModifyListener nameCompositeModifyListener;
100

    
101
	protected NameViewer nameViewer;
102

    
103
	private AbstractGroup group;
104

    
105
	private Label nonEditableInfoLabel;
106
	private DefaultToolTip nonEditableInfoHover;
107

    
108
	private static AbstractGroupedContainer selection;
109

    
110
	private FocusListener focusListener;
111
	private LineBreakListener lineBreakListener;
112

    
113
	private int cursorPosition;
114

    
115
	protected Composite control;
116

    
117
	private Color backgroundColor;
118
	private boolean isDirty;
119

    
120
	/**
121
	 * <p>
122
	 * Constructor for AbstractGroupedContainer.
123
	 * </p>
124
	 * 
125
	 * @param editor
126
	 *            a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor}
127
	 *            object.
128
	 * @param group
129
	 *            a
130
	 *            {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
131
	 *            object.
132
	 * @param taxonBase
133
	 *            a T object.
134
	 * @param <T>
135
	 *            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
		createTextViewer();
146
		createLineWrapSupport();
147
		createLineBreakListener();
148

    
149
		setMenu(getEditor().getMenu());
150

    
151
		setDraggableControl(new Control[] { getControl(),
152
				getNameViewer().getRulerControl() });
153

    
154
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
155

    
156
		initializeComposite();
157

    
158
		createListener();
159

    
160
		enableFreeText();
161
	}
162

    
163
	/**
164
	 * <p>
165
	 * createListener
166
	 * </p>
167
	 */
168
	protected void createListener() {
169
		nameCompositeModifyListener = new ModifyListener() {
170

    
171
			public void modifyText(ModifyEvent e) {
172
				// mark the composite dirty
173
				setDirty(true);
174
				// parse the text
175
				String text = nameViewer.getTextWidget().getText();
176

    
177
				NonViralName name = parseHandler.parse(text);
178
				getTaxonBase().setName(name);
179
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
180

    
181
				// show errors resulting from parsing
182
				calculateAnnotations();
183
				// store the position of the cursor
184
				storeCursor();
185
				// notify selection listener
186
				setDelayedSelection();
187
			}
188
		};
189
		nameCompositeFocusListener = new FocusAdapter() {
190

    
191
			/*
192
			 * (non-Javadoc)
193
			 * 
194
			 * @see
195
			 * org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt
196
			 * .events.FocusEvent)
197
			 */
198
			@Override
199
			public void focusLost(FocusEvent e) {
200
				super.focusLost(e);
201

    
202
				persistName();
203
			}
204
		};
205

    
206
		addListener();
207
	}
208

    
209
	private void addListener() {
210
		getNameViewer().getTextWidget().addModifyListener(
211
				nameCompositeModifyListener);
212
		getNameViewer().getTextWidget().addFocusListener(
213
				nameCompositeFocusListener);
214
	}
215

    
216
	private void removeListener() {
217
		getNameViewer().getTextWidget().removeModifyListener(
218
				nameCompositeModifyListener);
219
		getNameViewer().getTextWidget().removeFocusListener(
220
				nameCompositeFocusListener);
221
	}
222

    
223
	/**
224
	 * Initialize the composite specific code
225
	 */
226
	protected abstract void initializeComposite();
227

    
228
	/**
229
	 * <p>
230
	 * getEmptyTextPrompt
231
	 * </p>
232
	 * 
233
	 * @return a {@link java.lang.String} object.
234
	 */
235
	protected String getEmptyTextPrompt() {
236
		return EMPTY_NAME_PROMPT;
237
	}
238

    
239
	/**
240
	 * 
241
	 */
242
	private void showNameRelations() {
243
		TaxonNameBase<?, ?> name = getName();
244
		if (name == null) {
245
			return;
246
		}
247

    
248
		ILabelImageStrategy strategy = LabelImageProvider
249
				.getLabelStrategy(name);
250
		LabelImageProvider labelProvider = new LabelImageProvider(strategy);
251

    
252
		Set<NameRelationship> nameRelations = name.getNameRelations();
253
		if (nameRelations.size() == 0) {
254
			return;
255
		}
256
		// for (NameRelationship nameRelation : nameRelations) {
257
		// String typeLabel = null;
258
		// TaxonNameBase<?, ?> relatedName = null;
259
		//
260
		// if (name.equals(nameRelation.getFromName())) {
261
		// typeLabel = labelProvider.getNameRelationTypeLabel(
262
		// nameRelation.getType());
263
		// relatedName = nameRelation.getToName();
264
		// } else {
265
		// typeLabel = labelProvider.getNameRelationTypeInverseLabel(
266
		// nameRelation.getType());
267
		// relatedName = nameRelation.getFromName();
268
		// }
269
		//
270
		// setNonEditableInfo(typeLabel + " " +
271
		// NameHelper.getDisplayName(relatedName));
272
		// }
273
	}
274

    
275
	/**
276
	 * <p>
277
	 * initTextViewer
278
	 * </p>
279
	 */
280
	protected void initTextViewer() {
281

    
282
		// showNameRelations();
283

    
284
		updateIndent();
285

    
286
		updateIcon();
287

    
288
		String text = NameHelper.getDisplayNameWithRef(getData());
289

    
290
		if (text.length() == 0) {
291
			initEmptyText();
292
		} else {
293
			getNameViewer().setText(text);
294
			placeCursor();
295
		}
296
		calculateAnnotations();
297
	}
298

    
299
	/**
300
	 * <p>
301
	 * calculateErrors
302
	 * </p>
303
	 */
304
	synchronized protected void calculateAnnotations() {
305
		getNameViewer().clearAnnotations();
306
		showAnnotations();
307
	}
308

    
309
	/**
310
	 * 
311
	 */
312
	public void showAnnotations() {
313

    
314
		if (getName().hasProblem()) {
315
			showParsingProblems();
316
		}
317

    
318
		if (!isNameParsable()) {
319
			getNameViewer()
320
					.addAnnotation(
321
							new EditorAnnotation(EditorAnnotationType.WARNING,
322
									0,
323
									"This name may only be edited in the details view."));
324
		}
325

    
326
		if (isNameUsedMultipleTimes()) {
327
			getNameViewer().addAnnotation(
328
					new EditorAnnotation(EditorAnnotationType.WARNING, 0,
329
							"This taxons name is used multiple times."));
330
		}
331

    
332
	}
333

    
334
	/**
335
	 * 
336
	 */
337
	private void showParsingProblems() {
338
		String text = getNameViewer().getTextWidget().getText();
339

    
340
		List<ParserProblem> parsingProblems = getName().getParsingProblems();
341

    
342
		for (ParserProblem problem : parsingProblems) {
343
			getNameViewer().addAnnotation(new EditorAnnotation(problem),
344
					getParsingProblemPosition());
345
		}
346
	}
347

    
348
	private Position getParsingProblemPosition() {
349
		String text = getNameViewer().getTextWidget().getText();
350

    
351
		if (getName().hasProblem() && text.length() > 0) {
352
			int start = getName().getProblemStarts();
353
			int length = getName().getProblemEnds() - start;
354

    
355
			if (start == -1 || getName().getProblemEnds() == -1) {
356
				return null;
357
			}
358

    
359
			// Don't let squigglies try to draw beyond the end of the text
360
			if (text.length() < start + length) {
361
				length = text.length() - start;
362
			}
363

    
364
			return new Position(start, length);
365
		}
366
		return null;
367
	}
368

    
369
	/**
370
	 * <p>
371
	 * handleSplitText
372
	 * </p>
373
	 * 
374
	 * @param text
375
	 *            a {@link java.lang.String} object.
376
	 */
377
	protected void handleSplitText(String text) {
378
		// Create a synonym in a new homotypic group using text as name
379
		TaxonNameBase synonymName = ParseHandler
380
				.parseReferencedName(text, null);
381

    
382
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation(
383
				"New Heterotypic Synonym", getEditor().getUndoContext(),
384
				getEditor().getTaxon(), synonymName, getEditor()));
385
	}
386

    
387
	/**
388
	 * Refreshes the display with latest data from the model.
389
	 * 
390
	 * Note: Will not parse the text and not calculate errors!
391
	 */
392
	public void refresh() {
393
		// showNameRelations();
394

    
395
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
396

    
397
		if (getNameViewer().getTextWidget() == null) {
398
			// we might get here via dnd. Look slike it can be ignored
399
			return;
400
		}
401

    
402
		if (text.length() == 0) {
403
			initEmptyText();
404
		} else if (!getNameViewer().getTextWidget().getText().equals(text)) {
405
			removeListener();
406
			getNameViewer().getTextWidget().setText(text);
407
			addListener();
408
		}
409

    
410
		updateNonEditableInfo();
411

    
412
		updateIcon();
413
		// placeCursor();
414
		updateIndent();
415

    
416
		enableFreeText();
417
	}
418

    
419
	/**
420
	 * 
421
	 */
422
	protected abstract void updateIcon();
423

    
424
	protected abstract void updateIndent();
425

    
426
	/**
427
	 * <p>
428
	 * updateNonEditableInfo
429
	 * </p>
430
	 */
431
	protected abstract void updateNonEditableInfo();
432

    
433
	/**
434
	 * 
435
	 */
436
	private void enableFreeText() {
437
		setEnabled(isFreetextEditingAllowed());
438
	}
439

    
440
	/**
441
	 * Checks whether the freetext should be editable based on specific empty
442
	 * fields.
443
	 * 
444
	 * @return
445
	 */
446
	private boolean isFreetextEditingAllowed() {
447
		NonViralName name = (NonViralName) HibernateProxyHelper
448
				.deproxy(getName());
449
		boolean enableFreetext = true;
450

    
451
		enableFreetext |= isNameUsedMultipleTimes();
452
		enableFreetext &= isNameParsable();
453

    
454
		return enableFreetext;
455
	}
456

    
457
	private boolean isNameUsedMultipleTimes() {
458
		if (getName().getTaxonBases().size() > 1) {
459
			return true;
460
		}
461
		return false;
462
	}
463

    
464
	private boolean isNameParsable() {
465
		TaxonNameBase name = getName();
466

    
467
		boolean isParsable = true;
468
		isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); // taxonFieldsEmpty();
469

    
470
		if (name instanceof NonViralName) {
471
			NonViralName nonViralName = (NonViralName) name;
472
			isParsable &= !nonViralName.isProtectedAuthorshipCache();
473
			isParsable &= !nonViralName.isProtectedNameCache();
474
		}
475

    
476
		return isParsable;
477
	}
478

    
479
	/**
480
	 * Parse the text and calculate errors
481
	 */
482
	public void parseAndCalculateAnnotations() {
483
		removeListener();
484
		String unparsedNameString = getNameViewer().getTextWidget().getText();
485
		parseHandler.parse(unparsedNameString);
486
		addListener();
487
		calculateAnnotations();
488
	}
489

    
490
	/**
491
	 * <p>
492
	 * getTaxonBase
493
	 * </p>
494
	 * 
495
	 * @return the taxonBase
496
	 */
497
	public T getTaxonBase() {
498
		return getData();
499
	}
500

    
501
	/**
502
	 * <p>
503
	 * getName
504
	 * </p>
505
	 * 
506
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
507
	 */
508
	public TaxonNameBase getName() {
509
		return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase()
510
				.getName());
511
	}
512

    
513
	/**
514
	 * <p>
515
	 * persistName
516
	 * </p>
517
	 */
518
	public void persistName() {
519
		if (isDirty()) {
520
			getNameViewer().getTextWidget().setEnabled(false);
521
			final String unparsedNameString = getNameViewer().getTextWidget()
522
					.getText();
523
			// Job job = new Job("Persisting Name"){
524
			//
525
			// @Override
526
			// protected IStatus run(IProgressMonitor monitor) {
527
			//
528
			final NonViralName name = parseHandler
529
					.parseAndResolveDuplicates(unparsedNameString);
530
			//
531
			// Display.getDefault().asyncExec(new Runnable(){
532
			// public void run() {
533
			getTaxonBase().setName(name);
534
			getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
535
			setDirty(false);
536
			getNameViewer().getTextWidget().setEnabled(true);
537
			// };
538
			// });
539
			//
540
			//
541
			// return Status.OK_STATUS;
542
			// }
543
			//
544
			// };
545
			//
546
			// job.setPriority(Job.DECORATE);
547
			// job.schedule();
548
		}
549
	}
550

    
551
	/**
552
	 * <p>
553
	 * Getter for the field <code>group</code>.
554
	 * </p>
555
	 * 
556
	 * @return a
557
	 *         {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
558
	 *         object.
559
	 */
560
	public AbstractGroup getGroup() {
561
		if (group == null) {
562
			throw new IllegalStateException("Group shall not be null.");
563
		}
564
		return group;
565
	}
566

    
567
	/**
568
	 * <p>
569
	 * remove
570
	 * </p>
571
	 */
572
	public void remove() {
573
		getGroup().remove(this);
574
	}
575

    
576
	/**
577
	 * <p>
578
	 * createControl
579
	 * </p>
580
	 */
581
	protected void createControl() {
582
		control = getEditor().getToolkit().createComposite(
583
				getGroup().getControl());
584

    
585
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
586
		TableWrapLayout layout = new TableWrapLayout();
587
		layout.leftMargin = 0;
588
		layout.rightMargin = 0;
589
		layout.topMargin = 5;
590
		layout.bottomMargin = 5;
591

    
592
		layout.verticalSpacing = 0;
593
		layout.horizontalSpacing = 0;
594

    
595
		control.setLayout(layout);
596

    
597
	}
598

    
599
	/**
600
	 * @return
601
	 */
602
	protected TaxonNameEditor getEditor() {
603
		return getGroup().getEditor();
604
	}
605

    
606
	/**
607
	 * <p>
608
	 * Getter for the field <code>control</code>.
609
	 * </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>
619
	 * createLineWrapSupport
620
	 * </p>
621
	 */
622
	protected void createLineWrapSupport() {
623
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
624
	}
625

    
626
	/**
627
	 * <p>
628
	 * createTextViewer
629
	 * </p>
630
	 */
631
	protected void createTextViewer() {
632
		nameViewer = new NameViewer(control);
633

    
634
		focusListener = new FocusAdapter() {
635
			@Override
636
			public void focusGained(FocusEvent e) {
637
				if(!enabled){
638
					return;
639
				}
640
				for (AbstractGroupedContainer container : getEditor()
641
						.getGroupedContainers()) {
642
					container.colorSelected(NOT_SELECTED);
643
				}
644
				getEditor().getManagedForm().setInput(
645
						AbstractGroupedContainer.this);
646
				placeCursor();
647
				colorSelected(SELECTED_FOCUS);
648
			}
649
		};
650
		nameViewer.getTextWidget().addFocusListener(focusListener);
651

    
652
		//
653
		MouseAdapter mouseListener = new MouseAdapter() {
654
			@Override
655
			public void mouseDown(MouseEvent e) {
656
				storeCursor();
657
			}
658
		};
659
		control.addMouseListener(mouseListener);
660
		nameViewer.getRulerControl().addMouseListener(mouseListener);
661
		nameViewer.getTextWidget().addMouseListener(mouseListener);
662
	}
663

    
664
	/**
665
	 * <p>
666
	 * setIcon
667
	 * </p>
668
	 * 
669
	 * @param icon
670
	 *            a {@link org.eclipse.swt.graphics.Image} object.
671
	 */
672
	public void setIcon(Image icon) {
673
		getNameViewer().setIcon(icon);
674
	}
675

    
676
	/**
677
	 * <p>
678
	 * setIndent
679
	 * </p>
680
	 * 
681
	 * @param indent
682
	 *            a int.
683
	 */
684
	public void setIndent(int indent) {
685
		if (control.getLayout() instanceof TableWrapLayout) {
686
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
687
			layout.leftMargin = indent;
688
			layout.rightMargin = ACCEPTED_INDENT;
689
			control.setLayout(layout);
690
			control.layout();
691
		} else {
692
			new RuntimeException(
693
					"Couldn't indent - composite's layout must be TableWrapLayout.");
694
		}
695
	}
696

    
697
	/**
698
	 * <p>
699
	 * setSelected
700
	 * </p>
701
	 */
702
	public void setSelected() {
703
		getNameViewer().getTextWidget().setFocus();
704
	}
705

    
706
	/**
707
	 * <p>
708
	 * isSelected
709
	 * </p>
710
	 * 
711
	 * @return a boolean.
712
	 */
713
	public boolean isSelected() {
714
		return getEditor().getSelectedContainer() == this;
715
	}
716

    
717
	/**
718
	 * <p>
719
	 * colorSelected
720
	 * </p>
721
	 * 
722
	 * @param mode
723
	 *            a int.
724
	 */
725
	public void colorSelected(int mode) {
726
		if (!control.isDisposed()) {
727
			String colorString = null;
728

    
729
			switch (mode) {
730
			case SELECTED_FOCUS:
731
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
732
				break;
733
			case SELECTED_NO_FOCUS:
734
				colorString = Resources.COLOR_CONTROL_SELECTED;
735
				break;
736
			default:
737
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
738
			}
739

    
740
			backgroundColor = EditorUtil.getColor(colorString);
741

    
742
			setBackground(backgroundColor);
743
		}
744
	}
745

    
746
	/**
747
	 * <p>
748
	 * setDelayedSelection
749
	 * </p>
750
	 */
751
	protected void setDelayedSelection() {
752
		// TODO this might be done better
753
		// this is the quickest solution i could come up with and it improves
754
		// performance
755
		// please reimplement if you know better.
756
		selection = this;
757

    
758
		// start timer
759
		Display display = Display.getCurrent();
760
		Runnable runnable = new Runnable() {
761

    
762
			public void run() {
763
				getEditor().getManagedForm().setInput(selection);
764
			}
765
		};
766
		display.timerExec(1000, runnable);
767

    
768
	}
769

    
770
	/**
771
	 * <p>
772
	 * setBackground
773
	 * </p>
774
	 * 
775
	 * @param color
776
	 *            a {@link org.eclipse.swt.graphics.Color} object.
777
	 */
778
	public void setBackground(Color color) {
779
		control.setBackground(color);
780

    
781
		for (Control child : control.getChildren()) {
782
			child.setBackground(color);
783
		}
784

    
785
		getNameViewer().setBackground(color);
786
	}
787

    
788
	/*
789
	 * (non-Javadoc)
790
	 * 
791
	 * @see
792
	 * org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
793
	 */
794
	/**
795
	 * <p>
796
	 * setFont
797
	 * </p>
798
	 * 
799
	 * @param font
800
	 *            a {@link org.eclipse.swt.graphics.Font} object.
801
	 */
802
	public void setFont(Font font) {
803
		getNameViewer().getTextWidget().setFont(font);
804
	}
805

    
806
	/**
807
	 * <p>
808
	 * Getter for the field <code>nameViewer</code>.
809
	 * </p>
810
	 * 
811
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer}
812
	 *         object.
813
	 */
814
	public NameViewer getNameViewer() {
815
		if (nameViewer == null) {
816
			throw new RuntimeException(
817
					"The Name Viewer is corrupt for Name Container: "
818
							+ getTaxonBase().getName().getTitleCache());
819
		}
820
		return nameViewer;
821
	}
822

    
823
	/**
824
	 * If <code>textViewer</code> has already been set, it will show a
825
	 * <code>prompt</code> along the lines of
826
	 * "Click here to start entering data" when empty.
827
	 * 
828
	 * @param prompt
829
	 *            a {@link java.lang.String} object.
830
	 */
831
	public void createEmptyViewerPrompt(final String prompt) {
832

    
833
		Assert.isNotNull(getNameViewer());
834

    
835
		final StyledText textControl = getNameViewer().getTextWidget();
836
		final IDocument document = getNameViewer().getDocument();
837

    
838
		setFocusListener(new FocusListener() {
839

    
840
			public void focusGained(FocusEvent e) {
841
				if (document.get().equals(prompt)) {
842
					textControl.setFont(getViewerFont());
843
					document.set("");
844
				}
845
			}
846

    
847
			public void focusLost(FocusEvent e) {
848
				if (document.getLength() == 0) {
849
					initEmptyText();
850
				}
851
			}
852

    
853
		});
854
		textControl.addFocusListener(getFocusListener());
855

    
856
		if (document.getLength() == 0) {
857
			textControl.setFont(EditorUtil
858
					.getFont(Resources.FONT_DEFAULT_PROMPT));
859
			document.set(prompt);
860
		}
861
	}
862

    
863
	/**
864
	 * <p>
865
	 * getViewerFont
866
	 * </p>
867
	 * 
868
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
869
	 */
870
	abstract protected Font getViewerFont();
871

    
872
	/**
873
	 * <p>
874
	 * initEmptyText
875
	 * </p>
876
	 */
877
	protected void initEmptyText() {
878
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
879
		getNameViewer().getTextWidget().setFont(defaultFont);
880

    
881
		getNameViewer().getDocument().set(getEmptyTextPrompt());
882
		placeCursor();
883
	}
884

    
885
	/**
886
	 * <p>
887
	 * Setter for the field <code>focusListener</code>.
888
	 * </p>
889
	 * 
890
	 * @param focusListener
891
	 *            a {@link org.eclipse.swt.events.FocusListener} object.
892
	 */
893
	protected void setFocusListener(FocusListener focusListener) {
894
		this.focusListener = focusListener;
895
	}
896

    
897
	private FocusListener getFocusListener() {
898
		return focusListener;
899
	}
900

    
901
	/**
902
	 * <p>
903
	 * setDirty
904
	 * </p>
905
	 * 
906
	 * @param isDirty
907
	 *            a boolean.
908
	 */
909
	public void setDirty(boolean isDirty) {
910
		if (isDirty) {
911
			getEditor().getManagedForm().dirtyStateChanged();
912
		}
913
		this.isDirty = isDirty;
914
	}
915

    
916
	/**
917
	 * <p>
918
	 * isDirty
919
	 * </p>
920
	 * 
921
	 * @return a boolean.
922
	 */
923
	public boolean isDirty() {
924
		return isDirty;
925
	}
926

    
927
	/**
928
	 * <p>
929
	 * setMenu
930
	 * </p>
931
	 * 
932
	 * @param menu
933
	 *            a {@link org.eclipse.swt.widgets.Menu} object.
934
	 */
935
	public void setMenu(Menu menu) {
936
		control.setMenu(menu);
937

    
938
		getNameViewer().setMenu(menu);
939
	}
940

    
941
	private Control[] draggableControls;
942

    
943
	/**
944
	 * <p>
945
	 * setDraggableControl
946
	 * </p>
947
	 * 
948
	 * @param controls
949
	 *            an array of {@link org.eclipse.swt.widgets.Control} objects.
950
	 */
951
	protected void setDraggableControl(Control[] controls) {
952
		draggableControls = controls;
953
	}
954

    
955
	/**
956
	 * <p>
957
	 * setIsDraggable
958
	 * </p>
959
	 * 
960
	 * @param draggable
961
	 *            a boolean.
962
	 */
963
	public void setIsDraggable(boolean draggable) {
964

    
965
		if (draggable) {
966

    
967
			if (draggableControls == null) {
968
				throw new NullPointerException(
969
						"Draggable controls must be set to add draggability");
970
			}
971

    
972
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
973
			int operations = DND.DROP_MOVE;
974

    
975
			for (Control draggableControl : draggableControls) {
976
				DragSource dragSource = new DragSource(draggableControl,
977
						operations);
978
				dragSource.setTransfer(types);
979

    
980
				dragSource.addDragListener(new NameEditorDragListener(this));
981
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
982
						control));
983
			}
984
		}
985
	}
986

    
987
	private String nonEditableText;
988

    
989
	ControlListener nonEditableResizeListener = new ControlAdapter() {
990

    
991
		int width = 0;
992

    
993
		@Override
994
		public void controlResized(ControlEvent e) {
995
			if (nonEditableInfoLabel.getBounds().width == width) {
996
				return;
997
			}
998
			width = nonEditableInfoLabel.getBounds().width;
999
			if (nonEditableInfoLabel.getBounds().width > 0) {
1000
				nonEditableInfoLabel.setText(Dialog.shortenText(
1001
						nonEditableText.toUpperCase(), nonEditableInfoLabel));
1002
			}
1003
		}
1004
	};
1005

    
1006
	private String nonEditableHoverText;
1007

    
1008
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
1009

    
1010
	private T data;
1011

    
1012
	private boolean enabled;
1013

    
1014
	/**
1015
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
1016
	 * input field. For instance, NameComposites display things like name
1017
	 * relations, sec. references, etc. here.
1018
	 * 
1019
	 * @param info
1020
	 *            the text to display in the label
1021
	 * @param append
1022
	 *            whether the string should be appended to text that is already
1023
	 *            shown in the label
1024
	 */
1025
	public void setNonEditableInfo(String info, boolean append) {
1026
		// TODO non editable info should only be drawn once, when everything
1027
		// else is drawn
1028
		info = info.toUpperCase();
1029

    
1030
		if (append) {
1031
			nonEditableText += ", " + info;
1032
			nonEditableHoverText += "\n" + info;
1033
		} else {
1034
			nonEditableText = info;
1035
			nonEditableHoverText = info;
1036
		}
1037

    
1038
		if (nonEditableInfoLabel == null) {
1039
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(
1040
					control, "");
1041
			TableWrapData layoutData = new TableWrapData(
1042
					TableWrapData.FILL_GRAB, TableWrapData.TOP);
1043
			// Set indent to viewer ruler's width
1044
			if (getNameViewer().getRulerControl() != null) {
1045
				// TODO right justify
1046
				layoutData.indent = NameViewer.RULER_WIDTH;
1047
			}
1048
			nonEditableInfoLabel.setLayoutData(layoutData);
1049

    
1050
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
1051
					nonEditableInfoLabel) {
1052
				@Override
1053
				public String getLabelText() {
1054
					return nonEditableText.toUpperCase();
1055
				}
1056
			};
1057
			nonEditableInfoLabel
1058
					.addControlListener(nonEditableLabelEllipsisListener);
1059

    
1060
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
1061
			nonEditableInfoHover.setRespectDisplayBounds(true);
1062

    
1063
		}
1064
		nonEditableInfoHover.setText(nonEditableHoverText);
1065
		nonEditableInfoLabel.setText(nonEditableText);
1066

    
1067
		calculateAnnotations();
1068
	}
1069

    
1070
	/**
1071
	 * <p>
1072
	 * Getter for the field <code>data</code>.
1073
	 * </p>
1074
	 * 
1075
	 * @return a T object.
1076
	 */
1077
	public T getData() {
1078
		return data;
1079
	}
1080

    
1081
	/**
1082
	 * <p>
1083
	 * Setter for the field <code>data</code>.
1084
	 * </p>
1085
	 * 
1086
	 * @param data
1087
	 *            a T object.
1088
	 */
1089
	public void setData(T data) {
1090
		this.data = (T) HibernateProxyHelper.deproxy(data);
1091
	}
1092

    
1093
	/**
1094
	 * If the user hitting carriage return should cause something to happen -
1095
	 * i.e. the creation of a new composite - call this method and override the
1096
	 * method handleSplitText().
1097
	 */
1098
	protected void createLineBreakListener() {
1099
		lineBreakListener = new LineBreakListener() {
1100
			@Override
1101
			public void handleSplitText(String text) {
1102
				AbstractGroupedContainer.this.handleSplitText(text);
1103
			}
1104
		};
1105

    
1106
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
1107
	}
1108

    
1109
	abstract class LabelEllipsisListener extends ControlAdapter {
1110

    
1111
		private final Label label;
1112
		int width = 0;
1113

    
1114
		LabelEllipsisListener(Label label) {
1115
			this.label = label;
1116
		}
1117

    
1118
		abstract public String getLabelText();
1119

    
1120
		@Override
1121
		public void controlResized(ControlEvent e) {
1122
			if (label.getBounds().width == width) {
1123
				return;
1124
			}
1125
			width = label.getBounds().width;
1126
			if (label.getBounds().width > 0) {
1127
				label.setText(TextHelper.shortenText(getLabelText(), label));
1128
			}
1129
		}
1130
	}
1131

    
1132
	/**
1133
	 * <p>
1134
	 * storeCursor
1135
	 * </p>
1136
	 */
1137
	public void storeCursor() {
1138
		this.cursorPosition = getNameViewer().getCursorPosition();
1139
	}
1140

    
1141
	/**
1142
	 * Puts the cursor to the position it was last seen on or to the end of line
1143
	 * if no former position is known.
1144
	 */
1145
	public void placeCursor() {
1146
		if (cursorPosition == 0) {
1147
			getNameViewer().setCursorToEOL();
1148
		} else {
1149
			getNameViewer().setCursorPosition(cursorPosition);
1150
		}
1151
	}
1152

    
1153
	/**
1154
	 * <p>
1155
	 * Setter for the field <code>group</code>.
1156
	 * </p>
1157
	 * 
1158
	 * @param group
1159
	 *            a
1160
	 *            {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
1161
	 *            object.
1162
	 */
1163
	public void setGroup(AbstractGroup group) {
1164
		this.group = group;
1165
	}
1166

    
1167
	/**
1168
	 * <p>
1169
	 * restoreColor
1170
	 * </p>
1171
	 */
1172
	public void restoreColor() {
1173
		setBackground(backgroundColor);
1174
	}
1175

    
1176
	/*
1177
	 * (non-Javadoc)
1178
	 * 
1179
	 * @see
1180
	 * org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm
1181
	 * )
1182
	 */
1183
	@Override
1184
	public void initialize(IManagedForm form) {
1185
		// TODO Auto-generated method stub
1186

    
1187
	}
1188

    
1189
	/*
1190
	 * (non-Javadoc)
1191
	 * 
1192
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1193
	 */
1194
	@Override
1195
	public void dispose() {
1196
		if (getControl() != null) {
1197
			setMenu(null);
1198
			getControl().dispose();
1199
		}
1200
	}
1201

    
1202
	/*
1203
	 * (non-Javadoc)
1204
	 * 
1205
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1206
	 */
1207
	@Override
1208
	public void commit(boolean onSave) {
1209
		if (isDirty()) {
1210
			persistName();
1211
		}
1212
	}
1213

    
1214
	/*
1215
	 * (non-Javadoc)
1216
	 * 
1217
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1218
	 */
1219
	@Override
1220
	public boolean setFormInput(Object input) {
1221
		// TODO Auto-generated method stub
1222
		return false;
1223
	}
1224

    
1225
	/*
1226
	 * (non-Javadoc)
1227
	 * 
1228
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1229
	 */
1230
	@Override
1231
	public void setFocus() {
1232
		getNameViewer().getControl().setFocus();
1233
	}
1234

    
1235
	/*
1236
	 * (non-Javadoc)
1237
	 * 
1238
	 * @see org.eclipse.ui.forms.IFormPart#isStale()
1239
	 */
1240
	@Override
1241
	public boolean isStale() {
1242
		// TODO Auto-generated method stub
1243
		return false;
1244
	}
1245
	
1246
	public void setEnabled(boolean enabled) {
1247
		this.enabled = enabled;
1248
		Color color = enabled ? control.getForeground() : EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR);
1249
				
1250
		getNameViewer().getTextWidget().setEditable(enabled);
1251
		getNameViewer().getTextWidget().setForeground(color);
1252
	}
1253
	
1254
	public boolean getEnabled(){
1255
		return enabled;
1256
	}
1257
}
(2-2/19)