Project

General

Profile

Download (28.8 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.NameRelationship;
52
import eu.etaxonomy.cdm.model.name.NonViralName;
53
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
54
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
55
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
56
import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
57
import eu.etaxonomy.taxeditor.editor.EditorUtil;
58
import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
59
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation.EditorAnnotationType;
60
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragListener;
61
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragSourceEffect;
62
import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
63
import eu.etaxonomy.taxeditor.labels.ILabelImageStrategy;
64
import eu.etaxonomy.taxeditor.labels.LabelImageProvider;
65
import eu.etaxonomy.taxeditor.model.IElementHasDetails;
66
import eu.etaxonomy.taxeditor.model.NameHelper;
67
import eu.etaxonomy.taxeditor.model.TextHelper;
68
import eu.etaxonomy.taxeditor.parser.ParseHandler;
69
import eu.etaxonomy.taxeditor.preference.Resources;
70

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

    
98
	protected ParseHandler parseHandler;
99

    
100
	private FocusListener nameCompositeFocusListener;
101
	private ModifyListener nameCompositeModifyListener;
102

    
103
	protected NameViewer nameViewer;
104

    
105
	private AbstractGroup group;
106

    
107
	private Label nonEditableInfoLabel;
108
	private DefaultToolTip nonEditableInfoHover;
109

    
110
	private static AbstractGroupedContainer selection;
111

    
112
	private FocusListener focusListener;
113
	private LineBreakListener lineBreakListener;
114

    
115
	private int cursorPosition;
116

    
117
	protected Composite control;
118

    
119
	private Color backgroundColor;
120
	private boolean isDirty;
121

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

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

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

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

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

    
156
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
157

    
158
		initializeComposite();
159

    
160
		createListener();
161

    
162
		enableFreeText();
163
	}
164

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

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

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

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

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

    
204
				persistName();
205
			}
206
		};
207

    
208
		addListener();
209
	}
210

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

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

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

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

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

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

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

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

    
284
		// showNameRelations();
285

    
286
		updateIndent();
287

    
288
		updateIcon();
289

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

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

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

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

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

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

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

    
334
	}
335

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

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

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

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

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

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

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

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

    
386
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation(
387
				"New Heterotypic Synonym", getEditor().getUndoContext(),
388
				getEditor().getTaxon(), synonymName, getEditor()));
389
	}
390

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

    
399
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
400

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

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

    
414
		updateNonEditableInfo();
415

    
416
		updateIcon();
417
		// placeCursor();
418
		updateIndent();
419

    
420
		enableFreeText();
421
	}
422

    
423
	/**
424
	 * 
425
	 */
426
	protected abstract void updateIcon();
427

    
428
	protected abstract void updateIndent();
429

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

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

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

    
455
		enableFreetext |= isNameUsedMultipleTimes();
456
		enableFreetext &= isNameParsable();
457

    
458
		return enableFreetext;
459
	}
460

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

    
485
	private boolean isNameParsable() {
486
		TaxonNameBase name = getName();
487

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

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

    
497
		return isParsable;
498
	}
499

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

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

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

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

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

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

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

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

    
613
		layout.verticalSpacing = 0;
614
		layout.horizontalSpacing = 0;
615

    
616
		control.setLayout(layout);
617

    
618
	}
619

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

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

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

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

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

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

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

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

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

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

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

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

    
761
			backgroundColor = EditorUtil.getColor(colorString);
762

    
763
			setBackground(backgroundColor);
764
		}
765
	}
766

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

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

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

    
789
	}
790

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

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

    
806
		getNameViewer().setBackground(color);
807
	}
808

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

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

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

    
854
		Assert.isNotNull(getNameViewer());
855

    
856
		final StyledText textControl = getNameViewer().getTextWidget();
857
		final IDocument document = getNameViewer().getDocument();
858

    
859
		setFocusListener(new FocusListener() {
860

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

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

    
874
		});
875
		textControl.addFocusListener(getFocusListener());
876

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

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

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

    
902
		getNameViewer().getDocument().set(getEmptyTextPrompt());
903
	//	placeCursor();
904
	}
905

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

    
918
	private FocusListener getFocusListener() {
919
		return focusListener;
920
	}
921

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

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

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

    
959
		getNameViewer().setMenu(menu);
960
	}
961

    
962
	private Control[] draggableControls;
963

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

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

    
986
		if (draggable) {
987

    
988
			if (draggableControls == null) {
989
				throw new NullPointerException(
990
						"Draggable controls must be set to add draggability");
991
			}
992

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

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

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

    
1008
	private String nonEditableText;
1009

    
1010
	ControlListener nonEditableResizeListener = new ControlAdapter() {
1011

    
1012
		int width = 0;
1013

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

    
1027
	private String nonEditableHoverText;
1028

    
1029
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
1030

    
1031
	private T data;
1032

    
1033
	private boolean enabled;
1034

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

    
1051
		if (append) {
1052
			nonEditableText += ", " + info;
1053
			nonEditableHoverText += "\n" + info;
1054
		} else {
1055
			nonEditableText = info;
1056
			nonEditableHoverText = info;
1057
		}
1058

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

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

    
1081
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
1082
			nonEditableInfoHover.setRespectDisplayBounds(true);
1083

    
1084
		}
1085
		nonEditableInfoHover.setText(nonEditableHoverText);
1086
		nonEditableInfoLabel.setText(nonEditableText);
1087

    
1088
		calculateAnnotations();
1089
	}
1090

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

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

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

    
1126
			
1127
		};
1128

    
1129
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
1130
		//getNameViewer().getTextWidget().addKeyListener(lineBreakListener);
1131
	}
1132

    
1133
	abstract class LabelEllipsisListener extends ControlAdapter {
1134

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

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

    
1142
		abstract public String getLabelText();
1143

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

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

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

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

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

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

    
1211
	}
1212

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

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

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

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

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