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.apache.commons.lang.StringUtils;
17
import org.eclipse.core.runtime.Assert;
18
import org.eclipse.jface.dialogs.Dialog;
19
import org.eclipse.jface.text.IDocument;
20
import org.eclipse.jface.text.Position;
21
import org.eclipse.jface.window.DefaultToolTip;
22
import org.eclipse.swt.custom.StyledText;
23
import org.eclipse.swt.dnd.DND;
24
import org.eclipse.swt.dnd.DragSource;
25
import org.eclipse.swt.dnd.Transfer;
26
import org.eclipse.swt.events.ControlAdapter;
27
import org.eclipse.swt.events.ControlEvent;
28
import org.eclipse.swt.events.ControlListener;
29
import org.eclipse.swt.events.FocusAdapter;
30
import org.eclipse.swt.events.FocusEvent;
31
import org.eclipse.swt.events.FocusListener;
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.common.CdmBase;
52
import eu.etaxonomy.cdm.model.name.INonViralName;
53
import eu.etaxonomy.cdm.model.name.NameRelationship;
54
import eu.etaxonomy.cdm.model.name.TaxonName;
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.l10n.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
 */
96
abstract public class AbstractGroupedContainer<T extends TaxonBase> implements
97
		IFormPart, IContainerConstants, IElementHasDetails {
98

    
99
	protected ParseHandler parseHandler;
100

    
101
	private FocusListener nameCompositeFocusListener;
102
	private ModifyListener nameCompositeModifyListener;
103

    
104
	protected NameViewer nameViewer;
105

    
106
	private AbstractGroup group;
107

    
108
	private Label nonEditableInfoLabel;
109
	private DefaultToolTip nonEditableInfoHover;
110

    
111
	private static AbstractGroupedContainer selection;
112

    
113
	private FocusListener focusListener;
114
	private LineBreakListener lineBreakListener;
115

    
116
	private int cursorPosition;
117

    
118
	protected Composite control;
119

    
120
	private Color backgroundColor;
121
	private boolean isDirty;
122

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

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

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

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

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

    
157
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
158

    
159
		initializeComposite();
160

    
161
		createListener();
162

    
163
		enableFreeText();
164
	}
165

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

    
174
			@Override
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
				TaxonName name = (TaxonName)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
	protected void addListener() {
214
		getNameViewer().getTextWidget().addModifyListener(
215
				nameCompositeModifyListener);
216
		getNameViewer().getTextWidget().addFocusListener(
217
				nameCompositeFocusListener);
218
	}
219

    
220
	protected 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
		TaxonName 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
		// TaxonName 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() != null && 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
		TaxonName name = getName();
345
		if (name == null){
346
			return;
347
		}
348

    
349
		List<ParserProblem> parsingProblems = name.getParsingProblems();
350

    
351
		for (ParserProblem problem : parsingProblems) {
352
			getNameViewer().addAnnotation(new EditorAnnotation(problem),
353
					getParsingProblemPosition());
354
		}
355
	}
356

    
357
	private Position getParsingProblemPosition() {
358
		String text = getNameViewer().getTextWidget().getText();
359

    
360
		if (getName() != null && getName().hasProblem() && text.length() > 0) {
361
			int start = getName().getProblemStarts();
362
			int length = getName().getProblemEnds() - start;
363

    
364
			if (start == -1 || getName().getProblemEnds() == -1) {
365
				return null;
366
			}
367

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

    
380
	/**
381
	 * <p>
382
	 * handleSplitText
383
	 * </p>
384
	 *
385
	 * @param text
386
	 *            a {@link java.lang.String} object.
387
	 */
388
	protected void handleSplitText(String text) {
389
		// Create a synonym in a new homotypic group using text as name
390
		TaxonName synonymName = TaxonName.castAndDeproxy(
391
				ParseHandler.parseReferencedName(text, null));
392

    
393
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation(
394
				Messages.AbstractGroupedContainer_NEW_HETERO_SYNONYM, getEditor().getUndoContext(),
395
				getEditor().getTaxon(), synonymName, getEditor()));
396
	}
397

    
398
	/**
399
	 * Refreshes the display with latest data from the model.
400
	 *
401
	 * Note: Will not parse the text and not calculate errors!
402
	 */
403
	@Override
404
    public void refresh() {
405
		// showNameRelations();
406

    
407
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
408

    
409
		if (getNameViewer().getTextWidget() == null) {
410
			// we might get here via dnd. Look slike it can be ignored
411
			return;
412
		}
413

    
414
		if (text.length() == 0) {
415
			initEmptyText();
416
		} else if (!getNameViewer().getTextWidget().getText().equals(text)) {
417
			removeListener();
418
			getNameViewer().getTextWidget().setText(text);
419
			addListener();
420
		}
421

    
422
		updateNonEditableInfo();
423

    
424
		updateIcon();
425
		// placeCursor();
426
		updateIndent();
427

    
428
		enableFreeText();
429
	}
430

    
431
	/**
432
	 *
433
	 */
434
	protected abstract void updateIcon();
435

    
436
	protected abstract void updateIndent();
437

    
438
	/**
439
	 * <p>
440
	 * updateNonEditableInfo
441
	 * </p>
442
	 */
443
	protected abstract void updateNonEditableInfo();
444

    
445
	/**
446
	 *
447
	 */
448
	protected void enableFreeText() {
449
		setEnabled(isFreetextEditingAllowed());
450
	}
451

    
452
	/**
453
	 * Checks whether the freetext should be editable based on specific empty
454
	 * fields.
455
	 *
456
	 * @return
457
	 */
458
	private boolean isFreetextEditingAllowed() {
459
		boolean enableFreetext = true;
460

    
461
		enableFreetext |= isNameUsedMultipleTimes();
462
		enableFreetext &= isNameParsable();
463

    
464
		return enableFreetext;
465
	}
466

    
467
	/**
468
	 * Checks whether there are more than one, non-orphaned taxon bases
469
	 * attached to the taxon name
470
	 *
471
	 * @return
472
	 */
473
	private boolean isNameUsedMultipleTimes() {
474

    
475
		TaxonName name = getName();
476
		if (name != null){
477
			Set<TaxonBase> taxonBases = name.getTaxonBases();
478
			Iterator<TaxonBase> tbItr = taxonBases.iterator();
479
			int nonOrphanedTaxonBaseCount = taxonBases.size();
480
	
481
			while(tbItr.hasNext()) {
482
				TaxonBase<?> tb = tbItr.next();
483
				if(tb.isOrphaned()) {
484
					nonOrphanedTaxonBaseCount--;
485
				}
486
			}
487
			if(nonOrphanedTaxonBaseCount > 1) {
488
				return true;
489
			}
490
		}
491
		return false;
492
	}
493

    
494
	private boolean isNameParsable() {
495
		TaxonName name = getName();
496
		if (name == null){
497
			return false;
498
		}
499

    
500
		boolean isParsable = true;
501
		isParsable &= StringUtils.isBlank(name.getAppendedPhrase()); // taxonFieldsEmpty();
502

    
503
		isParsable &= !name.isProtectedAuthorshipCache();
504
		isParsable &= !name.isProtectedNameCache();
505

    
506
		return isParsable;
507
	}
508

    
509
	/**
510
	 * Parse the text and calculate errors
511
	 */
512
	public void parseAndCalculateAnnotations() {
513
		removeListener();
514
		String unparsedNameString = getNameViewer().getTextWidget().getText();
515
		parseHandler.parse(unparsedNameString);
516
		addListener();
517
		calculateAnnotations();
518
	}
519

    
520
	/**
521
	 * <p>
522
	 * getTaxonBase
523
	 * </p>
524
	 *
525
	 * @return the taxonBase
526
	 */
527
	public T getTaxonBase() {
528
		return getData();
529
	}
530

    
531
	/**
532
	 * <p>
533
	 * getName
534
	 * </p>
535
	 *
536
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
537
	 */
538
	public TaxonName getName() {
539
		return CdmBase.deproxy(getTaxonBase().getName());
540
	}
541

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

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

    
596
	/**
597
	 * <p>
598
	 * remove
599
	 * </p>
600
	 */
601
	public void remove() {
602
		getGroup().remove(this);
603
	}
604

    
605
	/**
606
	 * <p>
607
	 * createControl
608
	 * </p>
609
	 */
610
	protected void createControl() {
611
		control = getEditor().getToolkit().createComposite(
612
				getGroup().getControl());
613

    
614
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
615
		TableWrapLayout layout = new TableWrapLayout();
616
		layout.leftMargin = 0;
617
		layout.rightMargin = 0;
618
		layout.topMargin = 5;
619
		layout.bottomMargin = 5;
620

    
621
		layout.verticalSpacing = 0;
622
		layout.horizontalSpacing = 0;
623

    
624
		control.setLayout(layout);
625

    
626
	}
627

    
628
	/**
629
	 * @return
630
	 */
631
	protected TaxonNameEditor getEditor() {
632
		return getGroup().getEditor();
633
	}
634

    
635
	/**
636
	 * <p>
637
	 * Getter for the field <code>control</code>.
638
	 * </p>
639
	 *
640
	 * @return a {@link org.eclipse.swt.widgets.Composite} object.
641
	 */
642
	public Composite getControl() {
643
		return control;
644
	}
645

    
646
	/**
647
	 * <p>
648
	 * createLineWrapSupport
649
	 * </p>
650
	 */
651
	protected void createLineWrapSupport() {
652
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
653
	}
654

    
655
	/**
656
	 * <p>
657
	 * createTextViewer
658
	 * </p>
659
	 */
660
	protected void createTextViewer() {
661
		nameViewer = new NameViewer(control);
662

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

    
681
		//
682
		MouseAdapter mouseListener = new MouseAdapter() {
683
			@Override
684
			public void mouseDown(MouseEvent e) {
685
				storeCursor();
686
			}
687
		};
688
		control.addMouseListener(mouseListener);
689
		nameViewer.getRulerControl().addMouseListener(mouseListener);
690
		nameViewer.getTextWidget().addMouseListener(mouseListener);
691
	}
692

    
693
	/**
694
	 * <p>
695
	 * setIcon
696
	 * </p>
697
	 *
698
	 * @param icon
699
	 *            a {@link org.eclipse.swt.graphics.Image} object.
700
	 */
701
	public void setIcon(Image icon) {
702
		getNameViewer().setIcon(icon);
703
	}
704

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

    
726
	/**
727
	 * <p>
728
	 * setSelected
729
	 * </p>
730
	 */
731
	public void setSelected() {
732
		getNameViewer().getTextWidget().setFocus();
733
	}
734

    
735
	/**
736
	 * <p>
737
	 * isSelected
738
	 * </p>
739
	 *
740
	 * @return a boolean.
741
	 */
742
	public boolean isSelected() {
743
		return getEditor().getSelectedContainer() == this;
744
	}
745

    
746
	/**
747
	 * <p>
748
	 * colorSelected
749
	 * </p>
750
	 *
751
	 * @param mode
752
	 *            a int.
753
	 */
754
	public void colorSelected(int mode) {
755
		if (!control.isDisposed()) {
756
			String colorString = null;
757

    
758
			switch (mode) {
759
			case SELECTED_FOCUS:
760
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
761
				break;
762
			case SELECTED_NO_FOCUS:
763
				colorString = Resources.COLOR_CONTROL_SELECTED;
764
				break;
765
			default:
766
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
767
			}
768

    
769
			backgroundColor = EditorUtil.getColor(colorString);
770

    
771
			setBackground(backgroundColor);
772
		}
773
	}
774

    
775
	/**
776
	 * <p>
777
	 * setDelayedSelection
778
	 * </p>
779
	 */
780
	protected void setDelayedSelection() {
781
		// TODO this might be done better
782
		// this is the quickest solution i could come up with and it improves
783
		// performance
784
		// please reimplement if you know better.
785
		selection = this;
786

    
787
		// start timer
788
		Display display = Display.getCurrent();
789
		Runnable runnable = new Runnable() {
790

    
791
			@Override
792
            public void run() {
793
				getEditor().getManagedForm().setInput(selection);
794
			}
795
		};
796
		display.timerExec(1000, runnable);
797

    
798
	}
799

    
800
	/**
801
	 * <p>
802
	 * setBackground
803
	 * </p>
804
	 *
805
	 * @param color
806
	 *            a {@link org.eclipse.swt.graphics.Color} object.
807
	 */
808
	public void setBackground(Color color) {
809
		control.setBackground(color);
810

    
811
		for (Control child : control.getChildren()) {
812
			child.setBackground(color);
813
		}
814

    
815
		getNameViewer().setBackground(color);
816
	}
817

    
818
	/*
819
	 * (non-Javadoc)
820
	 *
821
	 * @see
822
	 * org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
823
	 */
824
	/**
825
	 * <p>
826
	 * setFont
827
	 * </p>
828
	 *
829
	 * @param font
830
	 *            a {@link org.eclipse.swt.graphics.Font} object.
831
	 */
832
	public void setFont(Font font) {
833
		getNameViewer().getTextWidget().setFont(font);
834
	}
835

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

    
853
	/**
854
	 * If <code>textViewer</code> has already been set, it will show a
855
	 * <code>prompt</code> along the lines of
856
	 * "Click here to start entering data" when empty.
857
	 *
858
	 * @param prompt
859
	 *            a {@link java.lang.String} object.
860
	 */
861
	public void createEmptyViewerPrompt(final String prompt) {
862

    
863
		Assert.isNotNull(getNameViewer());
864

    
865
		final StyledText textControl = getNameViewer().getTextWidget();
866
		final IDocument document = getNameViewer().getDocument();
867

    
868
		setFocusListener(new FocusListener() {
869

    
870
			@Override
871
            public void focusGained(FocusEvent e) {
872
				if (document.get().equals(prompt)) {
873
					textControl.setFont(getViewerFont());
874
					document.set(""); //$NON-NLS-1$
875
				}
876
			}
877

    
878
			@Override
879
            public void focusLost(FocusEvent e) {
880
				if (document.getLength() == 0) {
881
					initEmptyText();
882
				}
883
			}
884

    
885
		});
886
		textControl.addFocusListener(getFocusListener());
887

    
888
		if (document.getLength() == 0) {
889
			textControl.setFont(EditorUtil
890
					.getFont(Resources.FONT_DEFAULT_PROMPT));
891
			document.set(prompt);
892
		}
893
	}
894

    
895
	/**
896
	 * <p>
897
	 * getViewerFont
898
	 * </p>
899
	 *
900
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
901
	 */
902
	abstract protected Font getViewerFont();
903

    
904
	/**
905
	 * <p>
906
	 * initEmptyText
907
	 * </p>
908
	 */
909
	protected void initEmptyText() {
910
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
911
		getNameViewer().getTextWidget().setFont(defaultFont);
912

    
913
		getNameViewer().getDocument().set(getEmptyTextPrompt());
914
		placeCursor();
915
	}
916

    
917
	/**
918
	 * <p>
919
	 * Setter for the field <code>focusListener</code>.
920
	 * </p>
921
	 *
922
	 * @param focusListener
923
	 *            a {@link org.eclipse.swt.events.FocusListener} object.
924
	 */
925
	protected void setFocusListener(FocusListener focusListener) {
926
		this.focusListener = focusListener;
927
	}
928

    
929
	private FocusListener getFocusListener() {
930
		return focusListener;
931
	}
932

    
933
	/**
934
	 * <p>
935
	 * setDirty
936
	 * </p>
937
	 *
938
	 * @param isDirty
939
	 *            a boolean.
940
	 */
941
	public void setDirty(boolean isDirty) {
942
		if (isDirty) {
943
			getEditor().getManagedForm().dirtyStateChanged();
944
		}
945
		this.isDirty = isDirty;
946
	}
947

    
948
	/**
949
	 * <p>
950
	 * isDirty
951
	 * </p>
952
	 *
953
	 * @return a boolean.
954
	 */
955
	@Override
956
    public boolean isDirty() {
957
		return isDirty;
958
	}
959

    
960
	/**
961
	 * <p>
962
	 * setMenu
963
	 * </p>
964
	 *
965
	 * @param menu
966
	 *            a {@link org.eclipse.swt.widgets.Menu} object.
967
	 */
968
	public void setMenu(Menu menu) {
969
		control.setMenu(menu);
970

    
971
		getNameViewer().setMenu(menu);
972
	}
973

    
974
	private Control[] draggableControls;
975

    
976
	/**
977
	 * <p>
978
	 * setDraggableControl
979
	 * </p>
980
	 *
981
	 * @param controls
982
	 *            an array of {@link org.eclipse.swt.widgets.Control} objects.
983
	 */
984
	protected void setDraggableControl(Control[] controls) {
985
		draggableControls = controls;
986
	}
987

    
988
	/**
989
	 * <p>
990
	 * setIsDraggable
991
	 * </p>
992
	 *
993
	 * @param draggable
994
	 *            a boolean.
995
	 */
996
	public void setIsDraggable(boolean draggable) {
997

    
998
		if (draggable) {
999

    
1000
			if (draggableControls == null) {
1001
				throw new NullPointerException(
1002
						"Draggable controls must be set to add draggability"); //$NON-NLS-1$
1003
			}
1004

    
1005
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
1006
			int operations = DND.DROP_MOVE;
1007

    
1008
			for (Control draggableControl : draggableControls) {
1009
				DragSource dragSource = new DragSource(draggableControl,
1010
						operations);
1011
				dragSource.setTransfer(types);
1012

    
1013
				dragSource.addDragListener(new NameEditorDragListener(this));
1014
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
1015
						control));
1016
			}
1017
		}
1018
	}
1019

    
1020
	private String nonEditableText;
1021

    
1022
	ControlListener nonEditableResizeListener = new ControlAdapter() {
1023

    
1024
		int width = 0;
1025

    
1026
		@Override
1027
		public void controlResized(ControlEvent e) {
1028
			if (nonEditableInfoLabel.getBounds().width == width) {
1029
				return;
1030
			}
1031
			width = nonEditableInfoLabel.getBounds().width;
1032
			if (nonEditableInfoLabel.getBounds().width > 0) {
1033
				nonEditableInfoLabel.setText(Dialog.shortenText(
1034
						nonEditableText.toUpperCase(), nonEditableInfoLabel));
1035
			}
1036
		}
1037
	};
1038

    
1039
	private String nonEditableHoverText;
1040

    
1041
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
1042

    
1043
	private T data;
1044

    
1045
	private boolean enabled;
1046

    
1047
	/**
1048
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
1049
	 * input field. For instance, NameComposites display things like name
1050
	 * relations, sec. references, etc. here.
1051
	 *
1052
	 * @param info
1053
	 *            the text to display in the label
1054
	 * @param append
1055
	 *            whether the string should be appended to text that is already
1056
	 *            shown in the label
1057
	 */
1058
	public void setNonEditableInfo(String info, boolean append) {
1059
		// TODO non editable info should only be drawn once, when everything
1060
		// else is drawn
1061
		info = info.toUpperCase();
1062

    
1063
		if (append) {
1064
			nonEditableText += ", " + info; //$NON-NLS-1$
1065
			nonEditableHoverText += "\n" + info; //$NON-NLS-1$
1066
		} else {
1067
			nonEditableText = info;
1068
			nonEditableHoverText = info;
1069
		}
1070

    
1071
		if (nonEditableInfoLabel == null) {
1072
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(
1073
					control, ""); //$NON-NLS-1$
1074
			TableWrapData layoutData = new TableWrapData(
1075
					TableWrapData.FILL_GRAB, TableWrapData.TOP);
1076
			// Set indent to viewer ruler's width
1077
			if (getNameViewer().getRulerControl() != null) {
1078
				// TODO right justify
1079
				layoutData.indent = NameViewer.RULER_WIDTH;
1080
			}
1081
			nonEditableInfoLabel.setLayoutData(layoutData);
1082

    
1083
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
1084
					nonEditableInfoLabel) {
1085
				@Override
1086
				public String getLabelText() {
1087
					return nonEditableText.toUpperCase();
1088
				}
1089
			};
1090
			nonEditableInfoLabel
1091
					.addControlListener(nonEditableLabelEllipsisListener);
1092

    
1093
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
1094
			nonEditableInfoHover.setRespectDisplayBounds(true);
1095

    
1096
		}
1097
		nonEditableInfoHover.setText(nonEditableHoverText);
1098
		nonEditableInfoLabel.setText(nonEditableText);
1099

    
1100
		calculateAnnotations();
1101
	}
1102

    
1103
	/**
1104
	 * <p>
1105
	 * Getter for the field <code>data</code>.
1106
	 * </p>
1107
	 *
1108
	 * @return a T object.
1109
	 */
1110
	@Override
1111
    public T getData() {
1112
		return data;
1113
	}
1114

    
1115
	/**
1116
	 * <p>
1117
	 * Setter for the field <code>data</code>.
1118
	 * </p>
1119
	 *
1120
	 * @param data
1121
	 *            a T object.
1122
	 */
1123
	public void setData(T data) {
1124
		this.data = HibernateProxyHelper.deproxy(data);
1125
	}
1126

    
1127
	/**
1128
	 * If the user hitting carriage return should cause something to happen -
1129
	 * i.e. the creation of a new composite - call this method and override the
1130
	 * method handleSplitText().
1131
	 */
1132
	protected void createLineBreakListener() {
1133
		lineBreakListener = new LineBreakListener() {
1134
			@Override
1135
			public void handleSplitText(String text) {
1136
				AbstractGroupedContainer.this.handleSplitText(text);
1137
			}
1138

    
1139
		};
1140

    
1141
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
1142

    
1143
	}
1144

    
1145
	abstract class LabelEllipsisListener extends ControlAdapter {
1146

    
1147
		private final Label label;
1148
		int width = 0;
1149

    
1150
		LabelEllipsisListener(Label label) {
1151
			this.label = label;
1152
		}
1153

    
1154
		abstract public String getLabelText();
1155

    
1156
		@Override
1157
		public void controlResized(ControlEvent e) {
1158
			if (label.getBounds().width == width) {
1159
				return;
1160
			}
1161
			width = label.getBounds().width;
1162
			if (label.getBounds().width > 0) {
1163
				label.setText(TextHelper.shortenText(getLabelText(), label));
1164
			}
1165
		}
1166
	}
1167

    
1168
	/**
1169
	 * <p>
1170
	 * storeCursor
1171
	 * </p>
1172
	 */
1173
	public void storeCursor() {
1174
		this.cursorPosition = getNameViewer().getCursorPosition();
1175
	}
1176

    
1177
	/**
1178
	 * Puts the cursor to the position it was last seen on or to the end of line
1179
	 * if no former position is known.
1180
	 */
1181
	public void placeCursor() {
1182
		if (cursorPosition == 0) {
1183
			getNameViewer().setCursorToEOL();
1184
		} else {
1185
			getNameViewer().setCursorPosition(cursorPosition);
1186
		}
1187
	}
1188

    
1189
	/**
1190
	 * <p>
1191
	 * Setter for the field <code>group</code>.
1192
	 * </p>
1193
	 *
1194
	 * @param group
1195
	 *            a
1196
	 *            {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
1197
	 *            object.
1198
	 */
1199
	public void setGroup(AbstractGroup group) {
1200
		this.group = group;
1201
	}
1202

    
1203
	/**
1204
	 * <p>
1205
	 * restoreColor
1206
	 * </p>
1207
	 */
1208
	public void restoreColor() {
1209
		setBackground(backgroundColor);
1210
	}
1211

    
1212
	/*
1213
	 * (non-Javadoc)
1214
	 *
1215
	 * @see
1216
	 * org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm
1217
	 * )
1218
	 */
1219
	@Override
1220
	public void initialize(IManagedForm form) {
1221
		// TODO Auto-generated method stub
1222

    
1223
	}
1224

    
1225
	/*
1226
	 * (non-Javadoc)
1227
	 *
1228
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1229
	 */
1230
	@Override
1231
	public void dispose() {
1232
		if (getControl() != null) {
1233
			setMenu(null);
1234
			getControl().dispose();
1235
		}
1236
	}
1237

    
1238
	/*
1239
	 * (non-Javadoc)
1240
	 *
1241
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1242
	 */
1243
	@Override
1244
	public void commit(boolean onSave) {
1245
		if (isDirty()) {
1246
			persistName();
1247
		}
1248
	}
1249

    
1250
	/*
1251
	 * (non-Javadoc)
1252
	 *
1253
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1254
	 */
1255
	@Override
1256
	public boolean setFormInput(Object input) {
1257
		return false;
1258
	}
1259

    
1260
	/*
1261
	 * (non-Javadoc)
1262
	 *
1263
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1264
	 */
1265
	@Override
1266
	public void setFocus() {
1267
		getNameViewer().getControl().setFocus();
1268
	}
1269

    
1270
	/*
1271
	 * (non-Javadoc)
1272
	 *
1273
	 * @see org.eclipse.ui.forms.IFormPart#isStale()
1274
	 */
1275
	@Override
1276
	public boolean isStale() {
1277
		return false;
1278
	}
1279

    
1280
	public void setDisabled(boolean disabled) {
1281
		this.enabled = !disabled;
1282
		setEnabled(enabled);
1283
	}
1284

    
1285
	public void setEnabled(boolean enabled) {
1286
		this.enabled = enabled;
1287
		Color color = enabled ? control.getForeground() : EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR);
1288

    
1289
		getNameViewer().getTextWidget().setEditable(enabled);
1290
		getNameViewer().getTextWidget().setEnabled(enabled);
1291
		getNameViewer().getTextWidget().setForeground(color);
1292
	}
1293

    
1294
	public boolean isEnabled(){
1295
		return enabled;
1296
	}
1297
}
(2-2/19)