Project

General

Profile

Download (28.6 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.ModifyEvent;
32
import org.eclipse.swt.events.ModifyListener;
33
import org.eclipse.swt.events.MouseAdapter;
34
import org.eclipse.swt.events.MouseEvent;
35
import org.eclipse.swt.graphics.Color;
36
import org.eclipse.swt.graphics.Font;
37
import org.eclipse.swt.graphics.Image;
38
import org.eclipse.swt.widgets.Composite;
39
import org.eclipse.swt.widgets.Control;
40
import org.eclipse.swt.widgets.Display;
41
import org.eclipse.swt.widgets.Label;
42
import org.eclipse.swt.widgets.Menu;
43
import org.eclipse.ui.forms.IFormPart;
44
import org.eclipse.ui.forms.IManagedForm;
45
import org.eclipse.ui.forms.widgets.TableWrapData;
46
import org.eclipse.ui.forms.widgets.TableWrapLayout;
47

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

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

    
97
	protected ParseHandler parseHandler;
98

    
99
	private FocusListener nameCompositeFocusListener;
100
	private ModifyListener nameCompositeModifyListener;
101

    
102
	protected NameViewer nameViewer;
103

    
104
	private AbstractGroup group;
105

    
106
	private Label nonEditableInfoLabel;
107
	private DefaultToolTip nonEditableInfoHover;
108

    
109
	private static AbstractGroupedContainer selection;
110

    
111
	private FocusListener focusListener;
112
	private LineBreakListener lineBreakListener;
113

    
114
	private int cursorPosition;
115

    
116
	protected Composite control;
117

    
118
	private Color backgroundColor;
119
	private boolean isDirty;
120

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

    
143
	public void createContent() {
144
		createControl();
145

    
146
		createTextViewer();
147
		createLineWrapSupport();
148
		createLineBreakListener();
149

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

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

    
155
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
156

    
157
		initializeComposite();
158

    
159
		createListener();
160

    
161
		enableFreeText();
162
	}
163

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

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

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

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

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

    
203
				persistName();
204
			}
205
		};
206

    
207
		addListener();
208
	}
209

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

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

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

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

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

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

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

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

    
283
		// showNameRelations();
284

    
285
		updateIndent();
286

    
287
		updateIcon();
288

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

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

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

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

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

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

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

    
333
	}
334

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
411
		updateNonEditableInfo();
412

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

    
417
		enableFreeText();
418
	}
419

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

    
425
	protected abstract void updateIndent();
426

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

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

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

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

    
455
		return enableFreetext;
456
	}
457

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

    
482
	private boolean isNameParsable() {
483
		TaxonNameBase name = getName();
484

    
485
		boolean isParsable = true;
486
		isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); // taxonFieldsEmpty();
487

    
488
		if (name instanceof NonViralName) {
489
			NonViralName nonViralName = (NonViralName) name;
490
			isParsable &= !nonViralName.isProtectedAuthorshipCache();
491
			isParsable &= !nonViralName.isProtectedNameCache();
492
		}
493

    
494
		return isParsable;
495
	}
496

    
497
	/**
498
	 * Parse the text and calculate errors
499
	 */
500
	public void parseAndCalculateAnnotations() {
501
		removeListener();
502
		String unparsedNameString = getNameViewer().getTextWidget().getText();
503
		parseHandler.parse(unparsedNameString);
504
		addListener();
505
		calculateAnnotations();
506
	}
507

    
508
	/**
509
	 * <p>
510
	 * getTaxonBase
511
	 * </p>
512
	 * 
513
	 * @return the taxonBase
514
	 */
515
	public T getTaxonBase() {
516
		return getData();
517
	}
518

    
519
	/**
520
	 * <p>
521
	 * getName
522
	 * </p>
523
	 * 
524
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
525
	 */
526
	public TaxonNameBase getName() {
527
		return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase()
528
				.getName());
529
	}
530

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

    
569
	/**
570
	 * <p>
571
	 * Getter for the field <code>group</code>.
572
	 * </p>
573
	 * 
574
	 * @return a
575
	 *         {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
576
	 *         object.
577
	 */
578
	public AbstractGroup getGroup() {
579
		if (group == null) {
580
			throw new IllegalStateException("Group shall not be null.");
581
		}
582
		return group;
583
	}
584

    
585
	/**
586
	 * <p>
587
	 * remove
588
	 * </p>
589
	 */
590
	public void remove() {
591
		getGroup().remove(this);
592
	}
593

    
594
	/**
595
	 * <p>
596
	 * createControl
597
	 * </p>
598
	 */
599
	protected void createControl() {
600
		control = getEditor().getToolkit().createComposite(
601
				getGroup().getControl());
602

    
603
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
604
		TableWrapLayout layout = new TableWrapLayout();
605
		layout.leftMargin = 0;
606
		layout.rightMargin = 0;
607
		layout.topMargin = 5;
608
		layout.bottomMargin = 5;
609

    
610
		layout.verticalSpacing = 0;
611
		layout.horizontalSpacing = 0;
612

    
613
		control.setLayout(layout);
614

    
615
	}
616

    
617
	/**
618
	 * @return
619
	 */
620
	protected TaxonNameEditor getEditor() {
621
		return getGroup().getEditor();
622
	}
623

    
624
	/**
625
	 * <p>
626
	 * Getter for the field <code>control</code>.
627
	 * </p>
628
	 * 
629
	 * @return a {@link org.eclipse.swt.widgets.Composite} object.
630
	 */
631
	public Composite getControl() {
632
		return control;
633
	}
634

    
635
	/**
636
	 * <p>
637
	 * createLineWrapSupport
638
	 * </p>
639
	 */
640
	protected void createLineWrapSupport() {
641
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
642
	}
643

    
644
	/**
645
	 * <p>
646
	 * createTextViewer
647
	 * </p>
648
	 */
649
	protected void createTextViewer() {
650
		nameViewer = new NameViewer(control);
651

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

    
670
		//
671
		MouseAdapter mouseListener = new MouseAdapter() {
672
			@Override
673
			public void mouseDown(MouseEvent e) {
674
				storeCursor();
675
			}
676
		};
677
		control.addMouseListener(mouseListener);
678
		nameViewer.getRulerControl().addMouseListener(mouseListener);
679
		nameViewer.getTextWidget().addMouseListener(mouseListener);
680
	}
681

    
682
	/**
683
	 * <p>
684
	 * setIcon
685
	 * </p>
686
	 * 
687
	 * @param icon
688
	 *            a {@link org.eclipse.swt.graphics.Image} object.
689
	 */
690
	public void setIcon(Image icon) {
691
		getNameViewer().setIcon(icon);
692
	}
693

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

    
715
	/**
716
	 * <p>
717
	 * setSelected
718
	 * </p>
719
	 */
720
	public void setSelected() {
721
		getNameViewer().getTextWidget().setFocus();
722
	}
723

    
724
	/**
725
	 * <p>
726
	 * isSelected
727
	 * </p>
728
	 * 
729
	 * @return a boolean.
730
	 */
731
	public boolean isSelected() {
732
		return getEditor().getSelectedContainer() == this;
733
	}
734

    
735
	/**
736
	 * <p>
737
	 * colorSelected
738
	 * </p>
739
	 * 
740
	 * @param mode
741
	 *            a int.
742
	 */
743
	public void colorSelected(int mode) {
744
		if (!control.isDisposed()) {
745
			String colorString = null;
746

    
747
			switch (mode) {
748
			case SELECTED_FOCUS:
749
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
750
				break;
751
			case SELECTED_NO_FOCUS:
752
				colorString = Resources.COLOR_CONTROL_SELECTED;
753
				break;
754
			default:
755
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
756
			}
757

    
758
			backgroundColor = EditorUtil.getColor(colorString);
759

    
760
			setBackground(backgroundColor);
761
		}
762
	}
763

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

    
776
		// start timer
777
		Display display = Display.getCurrent();
778
		Runnable runnable = new Runnable() {
779

    
780
			public void run() {
781
				getEditor().getManagedForm().setInput(selection);
782
			}
783
		};
784
		display.timerExec(1000, runnable);
785

    
786
	}
787

    
788
	/**
789
	 * <p>
790
	 * setBackground
791
	 * </p>
792
	 * 
793
	 * @param color
794
	 *            a {@link org.eclipse.swt.graphics.Color} object.
795
	 */
796
	public void setBackground(Color color) {
797
		control.setBackground(color);
798

    
799
		for (Control child : control.getChildren()) {
800
			child.setBackground(color);
801
		}
802

    
803
		getNameViewer().setBackground(color);
804
	}
805

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

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

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

    
851
		Assert.isNotNull(getNameViewer());
852

    
853
		final StyledText textControl = getNameViewer().getTextWidget();
854
		final IDocument document = getNameViewer().getDocument();
855

    
856
		setFocusListener(new FocusListener() {
857

    
858
			public void focusGained(FocusEvent e) {
859
				if (document.get().equals(prompt)) {
860
					textControl.setFont(getViewerFont());
861
					document.set("");
862
				}
863
			}
864

    
865
			public void focusLost(FocusEvent e) {
866
				if (document.getLength() == 0) {
867
					initEmptyText();
868
				}
869
			}
870

    
871
		});
872
		textControl.addFocusListener(getFocusListener());
873

    
874
		if (document.getLength() == 0) {
875
			textControl.setFont(EditorUtil
876
					.getFont(Resources.FONT_DEFAULT_PROMPT));
877
			document.set(prompt);
878
		}
879
	}
880

    
881
	/**
882
	 * <p>
883
	 * getViewerFont
884
	 * </p>
885
	 * 
886
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
887
	 */
888
	abstract protected Font getViewerFont();
889

    
890
	/**
891
	 * <p>
892
	 * initEmptyText
893
	 * </p>
894
	 */
895
	protected void initEmptyText() {
896
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
897
		getNameViewer().getTextWidget().setFont(defaultFont);
898

    
899
		getNameViewer().getDocument().set(getEmptyTextPrompt());
900
		placeCursor();
901
	}
902

    
903
	/**
904
	 * <p>
905
	 * Setter for the field <code>focusListener</code>.
906
	 * </p>
907
	 * 
908
	 * @param focusListener
909
	 *            a {@link org.eclipse.swt.events.FocusListener} object.
910
	 */
911
	protected void setFocusListener(FocusListener focusListener) {
912
		this.focusListener = focusListener;
913
	}
914

    
915
	private FocusListener getFocusListener() {
916
		return focusListener;
917
	}
918

    
919
	/**
920
	 * <p>
921
	 * setDirty
922
	 * </p>
923
	 * 
924
	 * @param isDirty
925
	 *            a boolean.
926
	 */
927
	public void setDirty(boolean isDirty) {
928
		if (isDirty) {
929
			getEditor().getManagedForm().dirtyStateChanged();
930
		}
931
		this.isDirty = isDirty;
932
	}
933

    
934
	/**
935
	 * <p>
936
	 * isDirty
937
	 * </p>
938
	 * 
939
	 * @return a boolean.
940
	 */
941
	public boolean isDirty() {
942
		return isDirty;
943
	}
944

    
945
	/**
946
	 * <p>
947
	 * setMenu
948
	 * </p>
949
	 * 
950
	 * @param menu
951
	 *            a {@link org.eclipse.swt.widgets.Menu} object.
952
	 */
953
	public void setMenu(Menu menu) {
954
		control.setMenu(menu);
955

    
956
		getNameViewer().setMenu(menu);
957
	}
958

    
959
	private Control[] draggableControls;
960

    
961
	/**
962
	 * <p>
963
	 * setDraggableControl
964
	 * </p>
965
	 * 
966
	 * @param controls
967
	 *            an array of {@link org.eclipse.swt.widgets.Control} objects.
968
	 */
969
	protected void setDraggableControl(Control[] controls) {
970
		draggableControls = controls;
971
	}
972

    
973
	/**
974
	 * <p>
975
	 * setIsDraggable
976
	 * </p>
977
	 * 
978
	 * @param draggable
979
	 *            a boolean.
980
	 */
981
	public void setIsDraggable(boolean draggable) {
982

    
983
		if (draggable) {
984

    
985
			if (draggableControls == null) {
986
				throw new NullPointerException(
987
						"Draggable controls must be set to add draggability");
988
			}
989

    
990
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
991
			int operations = DND.DROP_MOVE;
992

    
993
			for (Control draggableControl : draggableControls) {
994
				DragSource dragSource = new DragSource(draggableControl,
995
						operations);
996
				dragSource.setTransfer(types);
997

    
998
				dragSource.addDragListener(new NameEditorDragListener(this));
999
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
1000
						control));
1001
			}
1002
		}
1003
	}
1004

    
1005
	private String nonEditableText;
1006

    
1007
	ControlListener nonEditableResizeListener = new ControlAdapter() {
1008

    
1009
		int width = 0;
1010

    
1011
		@Override
1012
		public void controlResized(ControlEvent e) {
1013
			if (nonEditableInfoLabel.getBounds().width == width) {
1014
				return;
1015
			}
1016
			width = nonEditableInfoLabel.getBounds().width;
1017
			if (nonEditableInfoLabel.getBounds().width > 0) {
1018
				nonEditableInfoLabel.setText(Dialog.shortenText(
1019
						nonEditableText.toUpperCase(), nonEditableInfoLabel));
1020
			}
1021
		}
1022
	};
1023

    
1024
	private String nonEditableHoverText;
1025

    
1026
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
1027

    
1028
	private T data;
1029

    
1030
	private boolean enabled;
1031

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

    
1048
		if (append) {
1049
			nonEditableText += ", " + info;
1050
			nonEditableHoverText += "\n" + info;
1051
		} else {
1052
			nonEditableText = info;
1053
			nonEditableHoverText = info;
1054
		}
1055

    
1056
		if (nonEditableInfoLabel == null) {
1057
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(
1058
					control, "");
1059
			TableWrapData layoutData = new TableWrapData(
1060
					TableWrapData.FILL_GRAB, TableWrapData.TOP);
1061
			// Set indent to viewer ruler's width
1062
			if (getNameViewer().getRulerControl() != null) {
1063
				// TODO right justify
1064
				layoutData.indent = NameViewer.RULER_WIDTH;
1065
			}
1066
			nonEditableInfoLabel.setLayoutData(layoutData);
1067

    
1068
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
1069
					nonEditableInfoLabel) {
1070
				@Override
1071
				public String getLabelText() {
1072
					return nonEditableText.toUpperCase();
1073
				}
1074
			};
1075
			nonEditableInfoLabel
1076
					.addControlListener(nonEditableLabelEllipsisListener);
1077

    
1078
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
1079
			nonEditableInfoHover.setRespectDisplayBounds(true);
1080

    
1081
		}
1082
		nonEditableInfoHover.setText(nonEditableHoverText);
1083
		nonEditableInfoLabel.setText(nonEditableText);
1084

    
1085
		calculateAnnotations();
1086
	}
1087

    
1088
	/**
1089
	 * <p>
1090
	 * Getter for the field <code>data</code>.
1091
	 * </p>
1092
	 * 
1093
	 * @return a T object.
1094
	 */
1095
	public T getData() {
1096
		return data;
1097
	}
1098

    
1099
	/**
1100
	 * <p>
1101
	 * Setter for the field <code>data</code>.
1102
	 * </p>
1103
	 * 
1104
	 * @param data
1105
	 *            a T object.
1106
	 */
1107
	public void setData(T data) {
1108
		this.data = (T) HibernateProxyHelper.deproxy(data);
1109
	}
1110

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

    
1124
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
1125
	}
1126

    
1127
	abstract class LabelEllipsisListener extends ControlAdapter {
1128

    
1129
		private final Label label;
1130
		int width = 0;
1131

    
1132
		LabelEllipsisListener(Label label) {
1133
			this.label = label;
1134
		}
1135

    
1136
		abstract public String getLabelText();
1137

    
1138
		@Override
1139
		public void controlResized(ControlEvent e) {
1140
			if (label.getBounds().width == width) {
1141
				return;
1142
			}
1143
			width = label.getBounds().width;
1144
			if (label.getBounds().width > 0) {
1145
				label.setText(TextHelper.shortenText(getLabelText(), label));
1146
			}
1147
		}
1148
	}
1149

    
1150
	/**
1151
	 * <p>
1152
	 * storeCursor
1153
	 * </p>
1154
	 */
1155
	public void storeCursor() {
1156
		this.cursorPosition = getNameViewer().getCursorPosition();
1157
	}
1158

    
1159
	/**
1160
	 * Puts the cursor to the position it was last seen on or to the end of line
1161
	 * if no former position is known.
1162
	 */
1163
	public void placeCursor() {
1164
		if (cursorPosition == 0) {
1165
			getNameViewer().setCursorToEOL();
1166
		} else {
1167
			getNameViewer().setCursorPosition(cursorPosition);
1168
		}
1169
	}
1170

    
1171
	/**
1172
	 * <p>
1173
	 * Setter for the field <code>group</code>.
1174
	 * </p>
1175
	 * 
1176
	 * @param group
1177
	 *            a
1178
	 *            {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup}
1179
	 *            object.
1180
	 */
1181
	public void setGroup(AbstractGroup group) {
1182
		this.group = group;
1183
	}
1184

    
1185
	/**
1186
	 * <p>
1187
	 * restoreColor
1188
	 * </p>
1189
	 */
1190
	public void restoreColor() {
1191
		setBackground(backgroundColor);
1192
	}
1193

    
1194
	/*
1195
	 * (non-Javadoc)
1196
	 * 
1197
	 * @see
1198
	 * org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm
1199
	 * )
1200
	 */
1201
	@Override
1202
	public void initialize(IManagedForm form) {
1203
		// TODO Auto-generated method stub
1204

    
1205
	}
1206

    
1207
	/*
1208
	 * (non-Javadoc)
1209
	 * 
1210
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1211
	 */
1212
	@Override
1213
	public void dispose() {
1214
		if (getControl() != null) {
1215
			setMenu(null);
1216
			getControl().dispose();
1217
		}
1218
	}
1219

    
1220
	/*
1221
	 * (non-Javadoc)
1222
	 * 
1223
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1224
	 */
1225
	@Override
1226
	public void commit(boolean onSave) {
1227
		if (isDirty()) {
1228
			persistName();
1229
		}
1230
	}
1231

    
1232
	/*
1233
	 * (non-Javadoc)
1234
	 * 
1235
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1236
	 */
1237
	@Override
1238
	public boolean setFormInput(Object input) {
1239
		return false;
1240
	}
1241

    
1242
	/*
1243
	 * (non-Javadoc)
1244
	 * 
1245
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1246
	 */
1247
	@Override
1248
	public void setFocus() {
1249
		getNameViewer().getControl().setFocus();
1250
	}
1251

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