Project

General

Profile

Download (29 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.Iterator;
13
import java.util.List;
14
import java.util.Set;
15

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

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

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

    
100
	protected ParseHandler parseHandler;
101

    
102
	private FocusListener nameCompositeFocusListener;
103
	private ModifyListener nameCompositeModifyListener;
104

    
105
	protected NameViewer nameViewer;
106

    
107
	private AbstractGroup group;
108

    
109
	private Label nonEditableInfoLabel;
110
	private DefaultToolTip nonEditableInfoHover;
111

    
112
	private static AbstractGroupedContainer selection;
113

    
114
	private FocusListener focusListener;
115
	private LineBreakListener lineBreakListener;
116

    
117
	private int cursorPosition;
118

    
119
	protected Composite control;
120

    
121
	private Color backgroundColor;
122
	private boolean isDirty;
123

    
124
	/**
125
	 * <p>
126
	 * Constructor for AbstractGroupedContainer.
127
	 * </p>
128
	 * 
129
	 * @param editor
130
	 *            a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor}
131
	 *            object.
132
	 * @param group
133
	 *            a
134
	 *            {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
135
	 *            object.
136
	 * @param taxonBase
137
	 *            a T object.
138
	 * @param <T>
139
	 *            a T object.
140
	 */
141
	public AbstractGroupedContainer(T taxonBase) {
142
		setData(taxonBase);
143
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
144
	}
145

    
146
	public void createContent() {
147
		createControl();
148

    
149
		createTextViewer();
150
		createLineWrapSupport();
151
		createLineBreakListener();
152

    
153
		setMenu(getEditor().getMenu());
154

    
155
		setDraggableControl(new Control[] { getControl(),
156
				getNameViewer().getRulerControl() });
157

    
158
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
159

    
160
		initializeComposite();
161

    
162
		createListener();
163

    
164
		enableFreeText();
165
	}
166

    
167
	/**
168
	 * <p>
169
	 * createListener
170
	 * </p>
171
	 */
172
	protected void createListener() {
173
		nameCompositeModifyListener = new ModifyListener() {
174

    
175
			public void modifyText(ModifyEvent e) {
176
				// mark the composite dirty
177
				setDirty(true);
178
				// parse the text
179
				String text = nameViewer.getTextWidget().getText();
180

    
181
				TaxonNameBase<?,?> name = (TaxonNameBase<?,?>)parseHandler.parse(text);
182
				getTaxonBase().setName(name);
183
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
184

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

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

    
206
				persistName();
207
			}
208
		};
209

    
210
		addListener();
211
	}
212

    
213
	private void addListener() {
214
		getNameViewer().getTextWidget().addModifyListener(
215
				nameCompositeModifyListener);
216
		getNameViewer().getTextWidget().addFocusListener(
217
				nameCompositeFocusListener);
218
	}
219

    
220
	private void removeListener() {
221
		getNameViewer().getTextWidget().removeModifyListener(
222
				nameCompositeModifyListener);
223
		getNameViewer().getTextWidget().removeFocusListener(
224
				nameCompositeFocusListener);
225
	}
226

    
227
	/**
228
	 * Initialize the composite specific code
229
	 */
230
	protected abstract void initializeComposite();
231

    
232
	/**
233
	 * <p>
234
	 * getEmptyTextPrompt
235
	 * </p>
236
	 * 
237
	 * @return a {@link java.lang.String} object.
238
	 */
239
	protected String getEmptyTextPrompt() {
240
		return EMPTY_NAME_PROMPT;
241
	}
242

    
243
	/**
244
	 * 
245
	 */
246
	private void showNameRelations() {
247
		TaxonNameBase<?, ?> name = getName();
248
		if (name == null) {
249
			return;
250
		}
251

    
252
		ILabelImageStrategy strategy = LabelImageProvider
253
				.getLabelStrategy(name);
254
		LabelImageProvider labelProvider = new LabelImageProvider(strategy);
255

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

    
279
	/**
280
	 * <p>
281
	 * initTextViewer
282
	 * </p>
283
	 */
284
	protected void initTextViewer() {
285

    
286
		// showNameRelations();
287

    
288
		updateIndent();
289

    
290
		updateIcon();
291

    
292
		String text = NameHelper.getDisplayNameWithRef(getData());
293

    
294
		if (text.length() == 0) {
295
			initEmptyText();
296
		} else {
297
			getNameViewer().setText(text);
298
			placeCursor();
299
		}
300
		calculateAnnotations();
301
	}
302

    
303
	/**
304
	 * <p>
305
	 * calculateErrors
306
	 * </p>
307
	 */
308
	synchronized protected void calculateAnnotations() {
309
		getNameViewer().clearAnnotations();
310
		showAnnotations();
311
	}
312

    
313
	/**
314
	 * 
315
	 */
316
	public void showAnnotations() {
317

    
318
		if (getName().hasProblem()) {
319
			showParsingProblems();
320
		}
321

    
322
		if (!isNameParsable()) {
323
			getNameViewer()
324
					.addAnnotation(
325
							new EditorAnnotation(EditorAnnotationType.WARNING,
326
									0,
327
									Messages.AbstractGroupedContainer_EDIT_IN_DETAILS_VIEW));
328
		}
329

    
330
		if (isNameUsedMultipleTimes()) {
331
			getNameViewer().addAnnotation(
332
					new EditorAnnotation(EditorAnnotationType.WARNING, 0,
333
							Messages.AbstractGroupedContainer_MULTIPLE_USE));
334
		}
335

    
336
	}
337

    
338
	/**
339
	 * 
340
	 */
341
	private void showParsingProblems() {
342
		String text = getNameViewer().getTextWidget().getText();
343

    
344
		List<ParserProblem> parsingProblems = getName().getParsingProblems();
345

    
346
		for (ParserProblem problem : parsingProblems) {
347
			getNameViewer().addAnnotation(new EditorAnnotation(problem),
348
					getParsingProblemPosition());
349
		}
350
	}
351

    
352
	private Position getParsingProblemPosition() {
353
		String text = getNameViewer().getTextWidget().getText();
354

    
355
		if (getName().hasProblem() && text.length() > 0) {
356
			int start = getName().getProblemStarts();
357
			int length = getName().getProblemEnds() - start;
358

    
359
			if (start == -1 || getName().getProblemEnds() == -1) {
360
				return null;
361
			}
362

    
363
			// Don't let squigglies try to draw beyond the end of the text
364
			if (text.length() < start + length) {
365
				length = text.length() - start;
366
			}
367
			if (length<0){
368
				return null;
369
			}
370
			return new Position(start, length);
371
		}
372
		return null;
373
	}
374

    
375
	/**
376
	 * <p>
377
	 * handleSplitText
378
	 * </p>
379
	 * 
380
	 * @param text
381
	 *            a {@link java.lang.String} object.
382
	 */
383
	protected void handleSplitText(String text) {
384
		// Create a synonym in a new homotypic group using text as name
385
		TaxonNameBase synonymName = ParseHandler
386
				.parseReferencedName(text, null);
387

    
388
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation(
389
				Messages.AbstractGroupedContainer_NEW_HETERO_SYNONYM, getEditor().getUndoContext(),
390
				getEditor().getTaxon(), synonymName, getEditor()));
391
	}
392

    
393
	/**
394
	 * Refreshes the display with latest data from the model.
395
	 * 
396
	 * Note: Will not parse the text and not calculate errors!
397
	 */
398
	public void refresh() {
399
		// showNameRelations();
400

    
401
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
402

    
403
		if (getNameViewer().getTextWidget() == null) {
404
			// we might get here via dnd. Look slike it can be ignored
405
			return;
406
		}
407

    
408
		if (text.length() == 0) {
409
			initEmptyText();
410
		} else if (!getNameViewer().getTextWidget().getText().equals(text)) {
411
			removeListener();
412
			getNameViewer().getTextWidget().setText(text);
413
			addListener();
414
		}
415

    
416
		updateNonEditableInfo();
417

    
418
		updateIcon();
419
		// placeCursor();
420
		updateIndent();
421

    
422
		enableFreeText();
423
	}
424

    
425
	/**
426
	 * 
427
	 */
428
	protected abstract void updateIcon();
429

    
430
	protected abstract void updateIndent();
431

    
432
	/**
433
	 * <p>
434
	 * updateNonEditableInfo
435
	 * </p>
436
	 */
437
	protected abstract void updateNonEditableInfo();
438

    
439
	/**
440
	 * 
441
	 */
442
	private void enableFreeText() {
443
		setEnabled(isFreetextEditingAllowed());
444
	}
445

    
446
	/**
447
	 * Checks whether the freetext should be editable based on specific empty
448
	 * fields.
449
	 * 
450
	 * @return
451
	 */
452
	private boolean isFreetextEditingAllowed() {
453
		NonViralName name = (NonViralName) HibernateProxyHelper
454
				.deproxy(getName());
455
		boolean enableFreetext = true;
456

    
457
		enableFreetext |= isNameUsedMultipleTimes();
458
		enableFreetext &= isNameParsable();
459

    
460
		return enableFreetext;
461
	}
462

    
463
	/**
464
	 * Checks whether there are more than one, non-orphaned taxon bases   
465
	 * attached to the taxon name
466
	 * 
467
	 * @return
468
	 */
469
	private boolean isNameUsedMultipleTimes() {
470
		
471
		Set<TaxonBase> taxonBases = getName().getTaxonBases();
472
		Iterator<TaxonBase> tbItr = taxonBases.iterator();
473
		int nonOrphanedTaxonBaseCount = taxonBases.size();
474
		
475
		while(tbItr.hasNext()) {
476
			TaxonBase tb = tbItr.next(); 
477
			if(tb.isOrphaned()) {
478
				nonOrphanedTaxonBaseCount--;
479
			}
480
		}
481
		if(nonOrphanedTaxonBaseCount > 1) {
482
			return true;
483
		}
484
		return false;
485
	}
486

    
487
	private boolean isNameParsable() {
488
		TaxonNameBase name = getName();
489

    
490
		boolean isParsable = true;
491
		isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); // taxonFieldsEmpty();
492

    
493
		if (name instanceof NonViralName) {
494
			NonViralName nonViralName = (NonViralName) name;
495
			isParsable &= !nonViralName.isProtectedAuthorshipCache();
496
			isParsable &= !nonViralName.isProtectedNameCache();
497
		}
498

    
499
		return isParsable;
500
	}
501

    
502
	/**
503
	 * Parse the text and calculate errors
504
	 */
505
	public void parseAndCalculateAnnotations() {
506
		removeListener();
507
		String unparsedNameString = getNameViewer().getTextWidget().getText();
508
		parseHandler.parse(unparsedNameString);
509
		addListener();
510
		calculateAnnotations();
511
	}
512

    
513
	/**
514
	 * <p>
515
	 * getTaxonBase
516
	 * </p>
517
	 * 
518
	 * @return the taxonBase
519
	 */
520
	public T getTaxonBase() {
521
		return getData();
522
	}
523

    
524
	/**
525
	 * <p>
526
	 * getName
527
	 * </p>
528
	 * 
529
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
530
	 */
531
	public TaxonNameBase getName() {
532
		return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase()
533
				.getName());
534
	}
535

    
536
	/**
537
	 * <p>
538
	 * persistName
539
	 * </p>
540
	 */
541
	public void persistName() {
542
		if (isDirty()) {
543
			getNameViewer().getTextWidget().setEnabled(false);
544
			final String unparsedNameString = getNameViewer().getTextWidget()
545
					.getText();
546
			// Job job = new Job("Persisting Name"){
547
			//
548
			// @Override
549
			// protected IStatus run(IProgressMonitor monitor) {
550
			//
551
			final TaxonNameBase<?,?> name = (TaxonNameBase<?,?>)parseHandler
552
					.parseAndResolveDuplicates(unparsedNameString);
553
			//
554
			// Display.getDefault().asyncExec(new Runnable(){
555
			// public void run() {
556
			getTaxonBase().setName(name);
557
			getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
558
			setDirty(false);
559
			getNameViewer().getTextWidget().setEnabled(true);
560
			// };
561
			// });
562
			//
563
			//
564
			// return Status.OK_STATUS;
565
			// }
566
			//
567
			// };
568
			//
569
			// job.setPriority(Job.DECORATE);
570
			// job.schedule();
571
		}
572
	}
573

    
574
	/**
575
	 * <p>
576
	 * Getter for the field <code>group</code>.
577
	 * </p>
578
	 * 
579
	 * @return a
580
	 *         {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
581
	 *         object.
582
	 */
583
	public AbstractGroup getGroup() {
584
		if (group == null) {
585
			throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
586
		}
587
		return group;
588
	}
589

    
590
	/**
591
	 * <p>
592
	 * remove
593
	 * </p>
594
	 */
595
	public void remove() {
596
		getGroup().remove(this);
597
	}
598

    
599
	/**
600
	 * <p>
601
	 * createControl
602
	 * </p>
603
	 */
604
	protected void createControl() {
605
		control = getEditor().getToolkit().createComposite(
606
				getGroup().getControl());
607

    
608
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
609
		TableWrapLayout layout = new TableWrapLayout();
610
		layout.leftMargin = 0;
611
		layout.rightMargin = 0;
612
		layout.topMargin = 5;
613
		layout.bottomMargin = 5;
614

    
615
		layout.verticalSpacing = 0;
616
		layout.horizontalSpacing = 0;
617

    
618
		control.setLayout(layout);
619

    
620
	}
621

    
622
	/**
623
	 * @return
624
	 */
625
	protected TaxonNameEditor getEditor() {
626
		return getGroup().getEditor();
627
	}
628

    
629
	/**
630
	 * <p>
631
	 * Getter for the field <code>control</code>.
632
	 * </p>
633
	 * 
634
	 * @return a {@link org.eclipse.swt.widgets.Composite} object.
635
	 */
636
	public Composite getControl() {
637
		return control;
638
	}
639

    
640
	/**
641
	 * <p>
642
	 * createLineWrapSupport
643
	 * </p>
644
	 */
645
	protected void createLineWrapSupport() {
646
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
647
	}
648

    
649
	/**
650
	 * <p>
651
	 * createTextViewer
652
	 * </p>
653
	 */
654
	protected void createTextViewer() {
655
		nameViewer = new NameViewer(control);
656

    
657
		focusListener = new FocusAdapter() {
658
			@Override
659
			public void focusGained(FocusEvent e) {
660
				if(!enabled){
661
					return;
662
				}
663
				for (AbstractGroupedContainer container : getEditor()
664
						.getGroupedContainers()) {
665
					container.colorSelected(NOT_SELECTED);
666
				}
667
				getEditor().getManagedForm().setInput(
668
						AbstractGroupedContainer.this);
669
				placeCursor();
670
				colorSelected(SELECTED_FOCUS);
671
			}
672
		};
673
		nameViewer.getTextWidget().addFocusListener(focusListener);
674

    
675
		//
676
		MouseAdapter mouseListener = new MouseAdapter() {
677
			@Override
678
			public void mouseDown(MouseEvent e) {
679
				storeCursor();
680
			}
681
		};
682
		control.addMouseListener(mouseListener);
683
		nameViewer.getRulerControl().addMouseListener(mouseListener);
684
		nameViewer.getTextWidget().addMouseListener(mouseListener);
685
	}
686

    
687
	/**
688
	 * <p>
689
	 * setIcon
690
	 * </p>
691
	 * 
692
	 * @param icon
693
	 *            a {@link org.eclipse.swt.graphics.Image} object.
694
	 */
695
	public void setIcon(Image icon) {
696
		getNameViewer().setIcon(icon);
697
	}
698

    
699
	/**
700
	 * <p>
701
	 * setIndent
702
	 * </p>
703
	 * 
704
	 * @param indent
705
	 *            a int.
706
	 */
707
	public void setIndent(int indent) {
708
		if (control.getLayout() instanceof TableWrapLayout) {
709
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
710
			layout.leftMargin = indent;
711
			layout.rightMargin = ACCEPTED_INDENT;
712
			control.setLayout(layout);
713
			control.layout();
714
		} else {
715
			new RuntimeException(
716
					"Couldn't indent - composite's layout must be TableWrapLayout."); //$NON-NLS-1$
717
		}
718
	}
719

    
720
	/**
721
	 * <p>
722
	 * setSelected
723
	 * </p>
724
	 */
725
	public void setSelected() {
726
		getNameViewer().getTextWidget().setFocus();
727
	}
728

    
729
	/**
730
	 * <p>
731
	 * isSelected
732
	 * </p>
733
	 * 
734
	 * @return a boolean.
735
	 */
736
	public boolean isSelected() {
737
		return getEditor().getSelectedContainer() == this;
738
	}
739

    
740
	/**
741
	 * <p>
742
	 * colorSelected
743
	 * </p>
744
	 * 
745
	 * @param mode
746
	 *            a int.
747
	 */
748
	public void colorSelected(int mode) {
749
		if (!control.isDisposed()) {
750
			String colorString = null;
751

    
752
			switch (mode) {
753
			case SELECTED_FOCUS:
754
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
755
				break;
756
			case SELECTED_NO_FOCUS:
757
				colorString = Resources.COLOR_CONTROL_SELECTED;
758
				break;
759
			default:
760
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
761
			}
762

    
763
			backgroundColor = EditorUtil.getColor(colorString);
764

    
765
			setBackground(backgroundColor);
766
		}
767
	}
768

    
769
	/**
770
	 * <p>
771
	 * setDelayedSelection
772
	 * </p>
773
	 */
774
	protected void setDelayedSelection() {
775
		// TODO this might be done better
776
		// this is the quickest solution i could come up with and it improves
777
		// performance
778
		// please reimplement if you know better.
779
		selection = this;
780

    
781
		// start timer
782
		Display display = Display.getCurrent();
783
		Runnable runnable = new Runnable() {
784

    
785
			public void run() {
786
				getEditor().getManagedForm().setInput(selection);
787
			}
788
		};
789
		display.timerExec(1000, runnable);
790

    
791
	}
792

    
793
	/**
794
	 * <p>
795
	 * setBackground
796
	 * </p>
797
	 * 
798
	 * @param color
799
	 *            a {@link org.eclipse.swt.graphics.Color} object.
800
	 */
801
	public void setBackground(Color color) {
802
		control.setBackground(color);
803

    
804
		for (Control child : control.getChildren()) {
805
			child.setBackground(color);
806
		}
807

    
808
		getNameViewer().setBackground(color);
809
	}
810

    
811
	/*
812
	 * (non-Javadoc)
813
	 * 
814
	 * @see
815
	 * org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
816
	 */
817
	/**
818
	 * <p>
819
	 * setFont
820
	 * </p>
821
	 * 
822
	 * @param font
823
	 *            a {@link org.eclipse.swt.graphics.Font} object.
824
	 */
825
	public void setFont(Font font) {
826
		getNameViewer().getTextWidget().setFont(font);
827
	}
828

    
829
	/**
830
	 * <p>
831
	 * Getter for the field <code>nameViewer</code>.
832
	 * </p>
833
	 * 
834
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer}
835
	 *         object.
836
	 */
837
	public NameViewer getNameViewer() {
838
		if (nameViewer == null) {
839
			throw new RuntimeException(
840
					"The Name Viewer is corrupt for Name Container: " //$NON-NLS-1$
841
							+ getTaxonBase().getName().getTitleCache());
842
		}
843
		return nameViewer;
844
	}
845

    
846
	/**
847
	 * If <code>textViewer</code> has already been set, it will show a
848
	 * <code>prompt</code> along the lines of
849
	 * "Click here to start entering data" when empty.
850
	 * 
851
	 * @param prompt
852
	 *            a {@link java.lang.String} object.
853
	 */
854
	public void createEmptyViewerPrompt(final String prompt) {
855

    
856
		Assert.isNotNull(getNameViewer());
857

    
858
		final StyledText textControl = getNameViewer().getTextWidget();
859
		final IDocument document = getNameViewer().getDocument();
860

    
861
		setFocusListener(new FocusListener() {
862

    
863
			public void focusGained(FocusEvent e) {
864
				if (document.get().equals(prompt)) {
865
					textControl.setFont(getViewerFont());
866
					document.set(""); //$NON-NLS-1$
867
				}
868
			}
869

    
870
			public void focusLost(FocusEvent e) {
871
				if (document.getLength() == 0) {
872
					initEmptyText();
873
				}
874
			}
875

    
876
		});
877
		textControl.addFocusListener(getFocusListener());
878

    
879
		if (document.getLength() == 0) {
880
			textControl.setFont(EditorUtil
881
					.getFont(Resources.FONT_DEFAULT_PROMPT));
882
			document.set(prompt);
883
		}
884
	}
885

    
886
	/**
887
	 * <p>
888
	 * getViewerFont
889
	 * </p>
890
	 * 
891
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
892
	 */
893
	abstract protected Font getViewerFont();
894

    
895
	/**
896
	 * <p>
897
	 * initEmptyText
898
	 * </p>
899
	 */
900
	protected void initEmptyText() {
901
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
902
		getNameViewer().getTextWidget().setFont(defaultFont);
903

    
904
		getNameViewer().getDocument().set(getEmptyTextPrompt());
905
		placeCursor();
906
	}
907

    
908
	/**
909
	 * <p>
910
	 * Setter for the field <code>focusListener</code>.
911
	 * </p>
912
	 * 
913
	 * @param focusListener
914
	 *            a {@link org.eclipse.swt.events.FocusListener} object.
915
	 */
916
	protected void setFocusListener(FocusListener focusListener) {
917
		this.focusListener = focusListener;
918
	}
919

    
920
	private FocusListener getFocusListener() {
921
		return focusListener;
922
	}
923

    
924
	/**
925
	 * <p>
926
	 * setDirty
927
	 * </p>
928
	 * 
929
	 * @param isDirty
930
	 *            a boolean.
931
	 */
932
	public void setDirty(boolean isDirty) {
933
		if (isDirty) {
934
			getEditor().getManagedForm().dirtyStateChanged();
935
		}
936
		this.isDirty = isDirty;
937
	}
938

    
939
	/**
940
	 * <p>
941
	 * isDirty
942
	 * </p>
943
	 * 
944
	 * @return a boolean.
945
	 */
946
	public boolean isDirty() {
947
		return isDirty;
948
	}
949

    
950
	/**
951
	 * <p>
952
	 * setMenu
953
	 * </p>
954
	 * 
955
	 * @param menu
956
	 *            a {@link org.eclipse.swt.widgets.Menu} object.
957
	 */
958
	public void setMenu(Menu menu) {
959
		control.setMenu(menu);
960

    
961
		getNameViewer().setMenu(menu);
962
	}
963

    
964
	private Control[] draggableControls;
965

    
966
	/**
967
	 * <p>
968
	 * setDraggableControl
969
	 * </p>
970
	 * 
971
	 * @param controls
972
	 *            an array of {@link org.eclipse.swt.widgets.Control} objects.
973
	 */
974
	protected void setDraggableControl(Control[] controls) {
975
		draggableControls = controls;
976
	}
977

    
978
	/**
979
	 * <p>
980
	 * setIsDraggable
981
	 * </p>
982
	 * 
983
	 * @param draggable
984
	 *            a boolean.
985
	 */
986
	public void setIsDraggable(boolean draggable) {
987

    
988
		if (draggable) {
989

    
990
			if (draggableControls == null) {
991
				throw new NullPointerException(
992
						"Draggable controls must be set to add draggability"); //$NON-NLS-1$
993
			}
994

    
995
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
996
			int operations = DND.DROP_MOVE;
997

    
998
			for (Control draggableControl : draggableControls) {
999
				DragSource dragSource = new DragSource(draggableControl,
1000
						operations);
1001
				dragSource.setTransfer(types);
1002

    
1003
				dragSource.addDragListener(new NameEditorDragListener(this));
1004
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
1005
						control));
1006
			}
1007
		}
1008
	}
1009

    
1010
	private String nonEditableText;
1011

    
1012
	ControlListener nonEditableResizeListener = new ControlAdapter() {
1013

    
1014
		int width = 0;
1015

    
1016
		@Override
1017
		public void controlResized(ControlEvent e) {
1018
			if (nonEditableInfoLabel.getBounds().width == width) {
1019
				return;
1020
			}
1021
			width = nonEditableInfoLabel.getBounds().width;
1022
			if (nonEditableInfoLabel.getBounds().width > 0) {
1023
				nonEditableInfoLabel.setText(Dialog.shortenText(
1024
						nonEditableText.toUpperCase(), nonEditableInfoLabel));
1025
			}
1026
		}
1027
	};
1028

    
1029
	private String nonEditableHoverText;
1030

    
1031
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
1032

    
1033
	private T data;
1034

    
1035
	private boolean enabled;
1036

    
1037
	/**
1038
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
1039
	 * input field. For instance, NameComposites display things like name
1040
	 * relations, sec. references, etc. here.
1041
	 * 
1042
	 * @param info
1043
	 *            the text to display in the label
1044
	 * @param append
1045
	 *            whether the string should be appended to text that is already
1046
	 *            shown in the label
1047
	 */
1048
	public void setNonEditableInfo(String info, boolean append) {
1049
		// TODO non editable info should only be drawn once, when everything
1050
		// else is drawn
1051
		info = info.toUpperCase();
1052

    
1053
		if (append) {
1054
			nonEditableText += ", " + info; //$NON-NLS-1$
1055
			nonEditableHoverText += "\n" + info; //$NON-NLS-1$
1056
		} else {
1057
			nonEditableText = info;
1058
			nonEditableHoverText = info;
1059
		}
1060

    
1061
		if (nonEditableInfoLabel == null) {
1062
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(
1063
					control, ""); //$NON-NLS-1$
1064
			TableWrapData layoutData = new TableWrapData(
1065
					TableWrapData.FILL_GRAB, TableWrapData.TOP);
1066
			// Set indent to viewer ruler's width
1067
			if (getNameViewer().getRulerControl() != null) {
1068
				// TODO right justify
1069
				layoutData.indent = NameViewer.RULER_WIDTH;
1070
			}
1071
			nonEditableInfoLabel.setLayoutData(layoutData);
1072

    
1073
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
1074
					nonEditableInfoLabel) {
1075
				@Override
1076
				public String getLabelText() {
1077
					return nonEditableText.toUpperCase();
1078
				}
1079
			};
1080
			nonEditableInfoLabel
1081
					.addControlListener(nonEditableLabelEllipsisListener);
1082

    
1083
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
1084
			nonEditableInfoHover.setRespectDisplayBounds(true);
1085

    
1086
		}
1087
		nonEditableInfoHover.setText(nonEditableHoverText);
1088
		nonEditableInfoLabel.setText(nonEditableText);
1089

    
1090
		calculateAnnotations();
1091
	}
1092

    
1093
	/**
1094
	 * <p>
1095
	 * Getter for the field <code>data</code>.
1096
	 * </p>
1097
	 * 
1098
	 * @return a T object.
1099
	 */
1100
	public T getData() {
1101
		return data;
1102
	}
1103

    
1104
	/**
1105
	 * <p>
1106
	 * Setter for the field <code>data</code>.
1107
	 * </p>
1108
	 * 
1109
	 * @param data
1110
	 *            a T object.
1111
	 */
1112
	public void setData(T data) {
1113
		this.data = (T) HibernateProxyHelper.deproxy(data);
1114
	}
1115

    
1116
	/**
1117
	 * If the user hitting carriage return should cause something to happen -
1118
	 * i.e. the creation of a new composite - call this method and override the
1119
	 * method handleSplitText().
1120
	 */
1121
	protected void createLineBreakListener() {
1122
		lineBreakListener = new LineBreakListener() {
1123
			@Override
1124
			public void handleSplitText(String text) {
1125
				AbstractGroupedContainer.this.handleSplitText(text);
1126
			}
1127

    
1128
		};
1129

    
1130
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
1131
		
1132
	}
1133

    
1134
	abstract class LabelEllipsisListener extends ControlAdapter {
1135

    
1136
		private final Label label;
1137
		int width = 0;
1138

    
1139
		LabelEllipsisListener(Label label) {
1140
			this.label = label;
1141
		}
1142

    
1143
		abstract public String getLabelText();
1144

    
1145
		@Override
1146
		public void controlResized(ControlEvent e) {
1147
			if (label.getBounds().width == width) {
1148
				return;
1149
			}
1150
			width = label.getBounds().width;
1151
			if (label.getBounds().width > 0) {
1152
				label.setText(TextHelper.shortenText(getLabelText(), label));
1153
			}
1154
		}
1155
	}
1156

    
1157
	/**
1158
	 * <p>
1159
	 * storeCursor
1160
	 * </p>
1161
	 */
1162
	public void storeCursor() {
1163
		this.cursorPosition = getNameViewer().getCursorPosition();
1164
	}
1165

    
1166
	/**
1167
	 * Puts the cursor to the position it was last seen on or to the end of line
1168
	 * if no former position is known.
1169
	 */
1170
	public void placeCursor() {
1171
		if (cursorPosition == 0) {
1172
			getNameViewer().setCursorToEOL();
1173
		} else {
1174
			getNameViewer().setCursorPosition(cursorPosition);
1175
		}
1176
	}
1177

    
1178
	/**
1179
	 * <p>
1180
	 * Setter for the field <code>group</code>.
1181
	 * </p>
1182
	 * 
1183
	 * @param group
1184
	 *            a
1185
	 *            {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
1186
	 *            object.
1187
	 */
1188
	public void setGroup(AbstractGroup group) {
1189
		this.group = group;
1190
	}
1191

    
1192
	/**
1193
	 * <p>
1194
	 * restoreColor
1195
	 * </p>
1196
	 */
1197
	public void restoreColor() {
1198
		setBackground(backgroundColor);
1199
	}
1200

    
1201
	/*
1202
	 * (non-Javadoc)
1203
	 * 
1204
	 * @see
1205
	 * org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm
1206
	 * )
1207
	 */
1208
	@Override
1209
	public void initialize(IManagedForm form) {
1210
		// TODO Auto-generated method stub
1211

    
1212
	}
1213

    
1214
	/*
1215
	 * (non-Javadoc)
1216
	 * 
1217
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1218
	 */
1219
	@Override
1220
	public void dispose() {
1221
		if (getControl() != null) {
1222
			setMenu(null);
1223
			getControl().dispose();
1224
		}
1225
	}
1226

    
1227
	/*
1228
	 * (non-Javadoc)
1229
	 * 
1230
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1231
	 */
1232
	@Override
1233
	public void commit(boolean onSave) {
1234
		if (isDirty()) {
1235
			persistName();
1236
		}
1237
	}
1238

    
1239
	/*
1240
	 * (non-Javadoc)
1241
	 * 
1242
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1243
	 */
1244
	@Override
1245
	public boolean setFormInput(Object input) {
1246
		return false;
1247
	}
1248

    
1249
	/*
1250
	 * (non-Javadoc)
1251
	 * 
1252
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1253
	 */
1254
	@Override
1255
	public void setFocus() {
1256
		getNameViewer().getControl().setFocus();
1257
	}
1258

    
1259
	/*
1260
	 * (non-Javadoc)
1261
	 * 
1262
	 * @see org.eclipse.ui.forms.IFormPart#isStale()
1263
	 */
1264
	@Override
1265
	public boolean isStale() {
1266
		return false;
1267
	}
1268
	
1269
	public void setDisabled(boolean disabled) {
1270
		this.enabled = !disabled;
1271
		setEnabled(enabled);
1272
	}
1273
	
1274
	public void setEnabled(boolean enabled) {
1275
		this.enabled = enabled;
1276
		Color color = enabled ? control.getForeground() : EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR);
1277
				
1278
		getNameViewer().getTextWidget().setEditable(enabled);
1279
		getNameViewer().getTextWidget().setEnabled(enabled);
1280
		getNameViewer().getTextWidget().setForeground(color);
1281
	}
1282
	
1283
	public boolean isEnabled(){
1284
		return enabled;
1285
	}
1286
}
(2-2/19)