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

    
72
/**
73
 * Formats <code>GroupedComposite</code> with cosmetic and layout properties
74
 * specific to the Editor. This should be used to maintain a consistent look and
75
 * feel for all Editor freetext area components, such as
76
 * DescriptionElementComposite.
77
 * <p>
78
 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the
79
 * contents of {@link #getData()} when the <code>GroupedComposite</code> gets
80
 * focus, i.e. to populate the property sheet with the data.
81
 * </p>
82
 * <p>
83
 * The <code>IManagedForm</code> is also required to have a <code>Taxon</code>
84
 * in its own <code>getData()</code>.
85
 * </p>
86
 * <p>
87
 * The <code>IManagedForm</code> can also used for drawing borders by calling
88
 * the method <code>createBorderSupport()</code>.
89
 * </p>
90
 * 
91
 * @author p.ciardelli
92
 * @author n.hoffmann
93
 * @created 02.06.2008
94
 * @version 1.0
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
			public void modifyText(ModifyEvent e) {
175
				// mark the composite dirty
176
				setDirty(true);
177
				// parse the text
178
				String text = nameViewer.getTextWidget().getText();
179

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

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

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

    
205
				persistName();
206
			}
207
		};
208

    
209
		addListener();
210
	}
211

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

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

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

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

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

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

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

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

    
285
		// showNameRelations();
286

    
287
		updateIndent();
288

    
289
		updateIcon();
290

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

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

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

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

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

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

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

    
335
	}
336

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

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

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

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

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

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

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

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

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

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

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

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

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

    
415
		updateNonEditableInfo();
416

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

    
421
		enableFreeText();
422
	}
423

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

    
429
	protected abstract void updateIndent();
430

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

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

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

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

    
459
		return enableFreetext;
460
	}
461

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

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

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

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

    
498
		return isParsable;
499
	}
500

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

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

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

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

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

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

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

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

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

    
617
		control.setLayout(layout);
618

    
619
	}
620

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

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

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

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

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

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

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

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

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

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

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

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

    
762
			backgroundColor = EditorUtil.getColor(colorString);
763

    
764
			setBackground(backgroundColor);
765
		}
766
	}
767

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

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

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

    
790
	}
791

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

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

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

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

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

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

    
855
		Assert.isNotNull(getNameViewer());
856

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

    
860
		setFocusListener(new FocusListener() {
861

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

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

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

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

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

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

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

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

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

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

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

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

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

    
963
	private Control[] draggableControls;
964

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

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

    
987
		if (draggable) {
988

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

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

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

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

    
1009
	private String nonEditableText;
1010

    
1011
	ControlListener nonEditableResizeListener = new ControlAdapter() {
1012

    
1013
		int width = 0;
1014

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

    
1028
	private String nonEditableHoverText;
1029

    
1030
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
1031

    
1032
	private T data;
1033

    
1034
	private boolean enabled;
1035

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

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

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

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

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

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

    
1089
		calculateAnnotations();
1090
	}
1091

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

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

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

    
1127
		};
1128

    
1129
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
1130
		
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)