Project

General

Profile

Download (24 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.e4.container;
11

    
12
import java.util.Iterator;
13
import java.util.List;
14
import java.util.Set;
15

    
16
import org.apache.commons.lang3.StringUtils;
17
import org.eclipse.core.runtime.Assert;
18
import org.eclipse.e4.ui.di.UISynchronize;
19
import org.eclipse.jface.dialogs.Dialog;
20
import org.eclipse.jface.text.IDocument;
21
import org.eclipse.jface.text.Position;
22
import org.eclipse.jface.window.DefaultToolTip;
23
import org.eclipse.swt.custom.StyledText;
24
import org.eclipse.swt.dnd.DND;
25
import org.eclipse.swt.dnd.DragSource;
26
import org.eclipse.swt.dnd.Transfer;
27
import org.eclipse.swt.events.ControlAdapter;
28
import org.eclipse.swt.events.ControlEvent;
29
import org.eclipse.swt.events.ControlListener;
30
import org.eclipse.swt.events.FocusAdapter;
31
import org.eclipse.swt.events.FocusEvent;
32
import org.eclipse.swt.events.FocusListener;
33
import org.eclipse.swt.events.ModifyEvent;
34
import org.eclipse.swt.events.ModifyListener;
35
import org.eclipse.swt.events.MouseAdapter;
36
import org.eclipse.swt.events.MouseEvent;
37
import org.eclipse.swt.graphics.Color;
38
import org.eclipse.swt.graphics.Font;
39
import org.eclipse.swt.graphics.Image;
40
import org.eclipse.swt.widgets.Composite;
41
import org.eclipse.swt.widgets.Control;
42
import org.eclipse.swt.widgets.Display;
43
import org.eclipse.swt.widgets.Label;
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.hibernate.HibernateProxyHelper;
50
import eu.etaxonomy.cdm.model.common.CdmBase;
51
import eu.etaxonomy.cdm.model.name.TaxonName;
52
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
53
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
54
import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
55
import eu.etaxonomy.taxeditor.editor.l10n.Messages;
56
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation;
57
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation.EditorAnnotationType;
58
import eu.etaxonomy.taxeditor.editor.name.container.IContainerConstants;
59
import eu.etaxonomy.taxeditor.editor.name.container.LineBreakListener;
60
import eu.etaxonomy.taxeditor.editor.name.container.LineWrapSupport;
61
import eu.etaxonomy.taxeditor.editor.name.e4.TaxonEditor;
62
import eu.etaxonomy.taxeditor.editor.name.e4.dnd.NameEditorDragListenerE4;
63
import eu.etaxonomy.taxeditor.editor.name.e4.dnd.NameEditorDragSourceEffect;
64
import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
65
import eu.etaxonomy.taxeditor.model.AbstractUtility;
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
 * @author pplitzner
91
 * @date Aug 24, 2017
92
 *
93
 * @param <T>
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
	public AbstractGroupedContainer(AbstractGroup group, T taxonBase) {
123
		setData(taxonBase);
124
		this.group = group;
125
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
126
	}
127

    
128
	public void createContent() {
129
		createControl();
130

    
131
		createTextViewer();
132
		createLineWrapSupport();
133
		createLineBreakListener();
134

    
135
		setMenu();
136

    
137
		setDraggableControl(new Control[] { getControl(),
138
				getNameViewer().getRulerControl() });
139

    
140
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
141

    
142
		initializeComposite();
143

    
144
		createListener();
145

    
146
		enableFreeText();
147
	}
148

    
149
	protected void createListener() {
150
		nameCompositeModifyListener = (ModifyEvent e)->{
151
				// mark the composite dirty
152
				setDirty(true);
153

    
154
				// parse the text
155
				String text = nameViewer.getTextWidget().getText();
156

    
157
				TaxonName name = (TaxonName)parseHandler.parse(text);
158
				getTaxonBase().setName(name);
159
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
160
//				EventUtility.postAsyncEvent(WorkbenchEventConstants.REFRESH_DETAILS, true);
161

    
162
				// show errors resulting from parsing
163
				calculateAnnotations();
164
				// store the position of the cursor
165
				storeCursor();
166
				// notify selection listener
167
				setDelayedSelection();
168
		};
169

    
170
		nameCompositeFocusListener = new FocusAdapter() {
171

    
172
			@Override
173
			public void focusLost(FocusEvent e) {
174
				super.focusLost(e);
175
				persistName();
176
			}
177
		};
178

    
179
		addListener();
180
	}
181

    
182
	protected void addListener() {
183
		getNameViewer().getTextWidget().addModifyListener(
184
				nameCompositeModifyListener);
185
		getNameViewer().getTextWidget().addFocusListener(
186
				nameCompositeFocusListener);
187
	}
188

    
189
	protected void removeListener() {
190
		getNameViewer().getTextWidget().removeModifyListener(
191
				nameCompositeModifyListener);
192
		getNameViewer().getTextWidget().removeFocusListener(
193
				nameCompositeFocusListener);
194
	}
195

    
196
	/**
197
	 * Initialize the composite specific code
198
	 */
199
	protected abstract void initializeComposite();
200

    
201
	protected String getEmptyTextPrompt() {
202
		return EMPTY_NAME_PROMPT;
203
	}
204

    
205
	protected void initTextViewer() {
206

    
207
		updateIndent();
208

    
209
		updateIcon();
210

    
211
		String text = NameHelper.getDisplayNameWithRef(getData());
212

    
213
		if (text.length() == 0) {
214
			initEmptyText();
215
		} else {
216
			getNameViewer().setText(text);
217
			placeCursor();
218
		}
219
		calculateAnnotations();
220
	}
221

    
222
	synchronized protected void calculateAnnotations() {
223
		getNameViewer().clearAnnotations();
224
		showAnnotations();
225
	}
226

    
227
	public void showAnnotations() {
228

    
229
		if (getName() != null && getName().hasProblem()) {
230
			showParsingProblems();
231
		}
232

    
233
		if (!isNameParsable()) {
234
			getNameViewer()
235
					.addAnnotation(
236
							new EditorAnnotation(EditorAnnotationType.WARNING,
237
									0,
238
									Messages.AbstractGroupedContainer_EDIT_IN_DETAILS_VIEW));
239
		}
240

    
241
		if (isNameUsedMultipleTimes()) {
242
			getNameViewer().addAnnotation(
243
					new EditorAnnotation(EditorAnnotationType.WARNING, 0,
244
							Messages.AbstractGroupedContainer_MULTIPLE_USE));
245
		}
246

    
247
	}
248

    
249
	private void showParsingProblems() {
250
		TaxonName name = getName();
251
		if (name == null){
252
			return;
253
		}
254

    
255
		List<ParserProblem> parsingProblems = name.getParsingProblems();
256

    
257
		for (ParserProblem problem : parsingProblems) {
258
			getNameViewer().addAnnotation(new EditorAnnotation(problem),
259
					getParsingProblemPosition());
260
		}
261
	}
262

    
263
	private Position getParsingProblemPosition() {
264
		String text = getNameViewer().getTextWidget().getText();
265

    
266
		if (getName() != null && getName().hasProblem() && text.length() > 0) {
267
			int start = getName().getProblemStarts();
268
			int length = getName().getProblemEnds() - start;
269

    
270
			if (start == -1 || getName().getProblemEnds() == -1) {
271
				return null;
272
			}
273

    
274
			// Don't let squigglies try to draw beyond the end of the text
275
			if (text.length() < start + length) {
276
				length = text.length() - start;
277
			}
278
			if (length<0){
279
				return null;
280
			}
281
			return new Position(start, length);
282
		}
283
		return null;
284
	}
285

    
286
	protected void handleSplitText(String text) {
287
		// Create a synonym in a new homotypic group using text as name
288
		TaxonName synonymName = TaxonName.castAndDeproxy(
289
				ParseHandler.parseReferencedName(text, null));
290

    
291
		AbstractUtility.executeOperation(new CreateSynonymInNewGroupOperation(
292
				Messages.AbstractGroupedContainer_NEW_HETERO_SYNONYM, getEditor().getUndoContext(),
293
				getEditor().getTaxon(), synonymName, getEditor()), group.getContext().get(UISynchronize.class));
294
	}
295

    
296
	/**
297
	 * Refreshes the display with latest data from the model.
298
	 *
299
	 * Note: Will not parse the text and not calculate errors!
300
	 */
301
	@Override
302
    public void refresh() {
303
		// showNameRelations();
304

    
305
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
306

    
307
		if (getNameViewer().getTextWidget() == null) {
308
			// we might get here via dnd. Look slike it can be ignored
309
			return;
310
		}
311

    
312
		if (text.length() == 0) {
313
			initEmptyText();
314
		} else if (!getNameViewer().getTextWidget().getText().equals(text)) {
315
			removeListener();
316
			getNameViewer().getTextWidget().setText(text);
317
			addListener();
318
		}
319

    
320
		updateNonEditableInfo();
321

    
322
		updateIcon();
323
		// placeCursor();
324
		updateIndent();
325
		setDelayedSelection();
326
		enableFreeText();
327
	}
328

    
329
	protected abstract void updateIcon();
330

    
331
	protected abstract void updateIndent();
332

    
333
	protected abstract void updateNonEditableInfo();
334

    
335
	protected void enableFreeText() {
336
		setEnabled(isFreetextEditingAllowed());
337
	}
338

    
339
	/**
340
	 * Checks whether the freetext should be editable based on specific empty
341
	 * fields.
342
	 *
343
	 * @return
344
	 */
345
	private boolean isFreetextEditingAllowed() {
346
		boolean enableFreetext = true;
347

    
348
		enableFreetext |= isNameUsedMultipleTimes();
349
		enableFreetext &= isNameParsable();
350

    
351
		return enableFreetext;
352
	}
353

    
354
	/**
355
	 * Checks whether there are more than one, non-orphaned taxon bases
356
	 * attached to the taxon name
357
	 *
358
	 * @return
359
	 */
360
	private boolean isNameUsedMultipleTimes() {
361

    
362
		TaxonName name = getName();
363
		if (name != null){
364
			Set<TaxonBase> taxonBases = name.getTaxonBases();
365
			Iterator<TaxonBase> tbItr = taxonBases.iterator();
366
			int nonOrphanedTaxonBaseCount = taxonBases.size();
367

    
368
			while(tbItr.hasNext()) {
369
				TaxonBase<?> tb = tbItr.next();
370
				if(tb.isOrphaned()) {
371
					nonOrphanedTaxonBaseCount--;
372
				}
373
			}
374
			if(nonOrphanedTaxonBaseCount > 1) {
375
				return true;
376
			}
377
		}
378
		return false;
379
	}
380

    
381
	private boolean isNameParsable() {
382
		TaxonName name = getName();
383
		if (name == null){
384
			return false;
385
		}
386

    
387
		boolean isParsable = true;
388
		isParsable &= StringUtils.isBlank(name.getAppendedPhrase()); // taxonFieldsEmpty();
389
        if (name.isProtectedAuthorshipCache()){
390
            isParsable &= nameAuthorsSet(name);
391
        }
392
        if (name.isProtectedNameCache()){
393
            isParsable &= nameEpithetsNotSet(name) && nameAuthorsSet(name);
394
        }
395

    
396
		return isParsable;
397
	}
398

    
399
    private boolean nameAuthorsSet(TaxonName name) {
400
       boolean result = true;
401
       result &= name.getBasionymAuthorship() == null && name.getCombinationAuthorship() == null && name.getExBasionymAuthorship() == null && name.getExCombinationAuthorship() == null;
402
       return result;
403
    }
404

    
405
    private boolean nameEpithetsNotSet(TaxonName name) {
406
        boolean result = true;
407
        result &= name.getGenusOrUninomial() == null && name.getInfraGenericEpithet() == null && name.getSpecificEpithet() == null && name.getInfraSpecificEpithet() == null;
408

    
409
        return result;
410
    }
411

    
412
    /**
413
	 * Parse the text and calculate errors
414
	 */
415
	public void parseAndCalculateAnnotations() {
416
		removeListener();
417
		String unparsedNameString = getNameViewer().getTextWidget().getText();
418
		parseHandler.parse(unparsedNameString);
419
		addListener();
420
		calculateAnnotations();
421
	}
422

    
423
	public T getTaxonBase() {
424
		return getData();
425
	}
426

    
427
	public TaxonName getName() {
428
		return CdmBase.deproxy(getTaxonBase().getName());
429
	}
430

    
431
	public void persistName() {
432
		if (isDirty()) {
433
			getNameViewer().getTextWidget().setEnabled(false);
434
			final String unparsedNameString = getNameViewer().getTextWidget()
435
					.getText();
436
			final TaxonName name = (TaxonName)parseHandler
437
					.parseAndResolveDuplicates(unparsedNameString);
438
			getTaxonBase().setName(name);
439
			getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
440
			setDirty(false);
441
			getNameViewer().getTextWidget().setEnabled(true);
442
		}
443
	}
444

    
445
	public AbstractGroup getGroup() {
446
		if (group == null) {
447
			throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
448
		}
449
		return group;
450
	}
451

    
452
	public void remove() {
453
		getGroup().remove(this);
454
	}
455

    
456
	protected void createControl() {
457
		control = getEditor().getToolkit().createComposite(
458
				getGroup().getControl());
459

    
460
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
461
		TableWrapLayout layout = new TableWrapLayout();
462
		layout.leftMargin = 0;
463
		layout.rightMargin = 0;
464
		layout.topMargin = 5;
465
		layout.bottomMargin = 5;
466

    
467
		layout.verticalSpacing = 0;
468
		layout.horizontalSpacing = 0;
469

    
470
		control.setLayout(layout);
471

    
472
	}
473

    
474
	protected TaxonEditor getEditor() {
475
		return getGroup().getEditor();
476
	}
477

    
478
	public Composite getControl() {
479
		return control;
480
	}
481

    
482
	protected void createLineWrapSupport() {
483
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
484
	}
485

    
486
	protected void createTextViewer() {
487
		nameViewer = new NameViewer(control);
488

    
489
		focusListener = new FocusAdapter() {
490
			@Override
491
			public void focusGained(FocusEvent e) {
492
			    if (getEditor()
493
                        .getGroupedContainers().isEmpty()){
494
			        return;
495
			    }
496

    
497
				for (AbstractGroupedContainer<?> container : getEditor()
498
						.getGroupedContainers()) {
499
					container.colorSelected(NOT_SELECTED);
500
				}
501
				getEditor().getManagedForm().setInput(
502
						AbstractGroupedContainer.this);
503
				placeCursor();
504
				colorSelected(SELECTED_FOCUS);
505
			}
506
		};
507
		nameViewer.getTextWidget().addFocusListener(focusListener);
508

    
509
		MouseAdapter mouseListener = new MouseAdapter() {
510
			@Override
511
			public void mouseDown(MouseEvent e) {
512
				storeCursor();
513
			}
514
		};
515
		control.addMouseListener(mouseListener);
516
		nameViewer.getRulerControl().addMouseListener(mouseListener);
517
		nameViewer.getTextWidget().addMouseListener(mouseListener);
518
	}
519

    
520
	public void setIcon(Image icon) {
521
		getNameViewer().setIcon(icon);
522
	}
523

    
524
	public void setIndent(int indent) {
525
		if (control.getLayout() instanceof TableWrapLayout) {
526
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
527
			layout.leftMargin = indent;
528
			layout.rightMargin = ACCEPTED_INDENT;
529
			control.setLayout(layout);
530
			control.layout();
531
		} else {
532
			throw new RuntimeException(
533
					"Couldn't indent - composite's layout must be TableWrapLayout."); //$NON-NLS-1$
534
		}
535
	}
536

    
537
	public void setSelected() {
538
		getNameViewer().getTextWidget().setFocus();
539
	}
540

    
541
	public boolean isSelected() {
542
		return getEditor().getSelectedContainer() == this;
543
	}
544

    
545
	public void colorSelected(int mode) {
546
		if (control!=null && !control.isDisposed()) {
547
			String colorString = null;
548

    
549
			switch (mode) {
550
			case SELECTED_FOCUS:
551
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
552
				break;
553
			case SELECTED_NO_FOCUS:
554
				colorString = Resources.COLOR_CONTROL_SELECTED;
555
				break;
556
			default:
557
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
558
			}
559

    
560
			backgroundColor = AbstractUtility.getColor(colorString);
561

    
562
			setBackground(backgroundColor);
563
		}
564
	}
565

    
566
	private Delay delay = new Delay(Display.getCurrent());
567

    
568
	private class Delay extends Thread{
569

    
570
	    private volatile boolean stop = false;
571
        private Display display;
572

    
573
        public Delay(Display display) {
574
            this.display = display;
575
        }
576
        @Override
577
        public void run() {
578

    
579
            while (!stop) {
580
                try {
581
                    // my code goes here
582
                    Thread.sleep(1000);
583
                    if(!stop){
584
                        display.syncExec(() -> {
585
                                getEditor().getManagedForm().setInput(selection);
586
                        });
587
                        stop = true;
588
                    }
589
                } catch (InterruptedException ex) {
590
                    stop = true;
591
                }
592
            }
593
        }
594
        public void requestStop() {
595
            stop = true;
596
        }
597
	}
598

    
599
	protected void setDelayedSelection() {
600
		selection = this;
601

    
602
		Display display = Display.getCurrent();
603

    
604
		delay.requestStop();
605
		delay = new Delay(display);
606
		delay.start();
607

    
608
	}
609

    
610
	public void setBackground(Color color) {
611
		control.setBackground(color);
612

    
613
		for (Control child : control.getChildren()) {
614
			child.setBackground(color);
615
		}
616

    
617
		getNameViewer().setBackground(color);
618
	}
619

    
620
	public void setFont(Font font) {
621
		getNameViewer().getTextWidget().setFont(font);
622
	}
623

    
624
	public NameViewer getNameViewer() {
625
		if (nameViewer == null) {
626
			throw new RuntimeException(
627
					"The Name Viewer is corrupt for Name Container: " //$NON-NLS-1$
628
							+ getTaxonBase().getName().getTitleCache());
629
		}
630
		return nameViewer;
631
	}
632

    
633
	public void createEmptyViewerPrompt(final String prompt) {
634

    
635
		Assert.isNotNull(getNameViewer());
636

    
637
		final StyledText textControl = getNameViewer().getTextWidget();
638
		final IDocument document = getNameViewer().getDocument();
639

    
640
		setFocusListener(new FocusListener() {
641

    
642
			@Override
643
            public void focusGained(FocusEvent e) {
644
				if (document.get().equals(prompt)) {
645
					textControl.setFont(getViewerFont());
646
					document.set(""); //$NON-NLS-1$
647
				}
648
			}
649

    
650
			@Override
651
            public void focusLost(FocusEvent e) {
652
				if (document.getLength() == 0) {
653
					initEmptyText();
654
				}
655
			}
656

    
657
		});
658
		textControl.addFocusListener(getFocusListener());
659

    
660
		if (document.getLength() == 0) {
661
			textControl.setFont(AbstractUtility
662
					.getFont(Resources.FONT_DEFAULT_PROMPT));
663
			document.set(prompt);
664
		}
665
	}
666

    
667
	abstract protected Font getViewerFont();
668

    
669
	protected void initEmptyText() {
670
		Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
671
		getNameViewer().getTextWidget().setFont(defaultFont);
672

    
673
		getNameViewer().getDocument().set(getEmptyTextPrompt());
674
	}
675

    
676
	protected void setFocusListener(FocusListener focusListener) {
677
		this.focusListener = focusListener;
678
	}
679

    
680
	private FocusListener getFocusListener() {
681
		return focusListener;
682
	}
683

    
684
	public void setDirty(boolean isDirty) {
685
		if (isDirty) {
686
			getEditor().getManagedForm().dirtyStateChanged();
687
		}
688
		this.isDirty = isDirty;
689
	}
690

    
691
	@Override
692
    public boolean isDirty() {
693
		return isDirty;
694
	}
695

    
696
	public void setMenu() {
697
        getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
698
	}
699

    
700
	private Control[] draggableControls;
701

    
702
	protected void setDraggableControl(Control[] controls) {
703
		draggableControls = controls;
704
	}
705

    
706
	public void setIsDraggable(boolean draggable) {
707

    
708
		if (draggable) {
709

    
710
			if (draggableControls == null) {
711
				throw new NullPointerException(
712
						"Draggable controls must be set to add draggability"); //$NON-NLS-1$
713
			}
714

    
715
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
716
			int operations = DND.DROP_MOVE;
717

    
718
			for (Control draggableControl : draggableControls) {
719
				DragSource dragSource = new DragSource(draggableControl,
720
						operations);
721
				dragSource.setTransfer(types);
722

    
723
				dragSource.addDragListener(new NameEditorDragListenerE4(this));
724
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
725
						control));
726
			}
727
		}
728
	}
729

    
730
	private String nonEditableText;
731

    
732
	ControlListener nonEditableResizeListener = new ControlAdapter() {
733

    
734
		int width = 0;
735

    
736
		@Override
737
		public void controlResized(ControlEvent e) {
738
			if (nonEditableInfoLabel.getBounds().width == width) {
739
				return;
740
			}
741
			width = nonEditableInfoLabel.getBounds().width;
742
			if (nonEditableInfoLabel.getBounds().width > 0) {
743
				nonEditableInfoLabel.setText(Dialog.shortenText(
744
						nonEditableText.toUpperCase(), nonEditableInfoLabel));
745
			}
746
		}
747
	};
748

    
749
	private String nonEditableHoverText;
750

    
751
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
752

    
753
	private T data;
754

    
755
	/**
756
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
757
	 * input field. For instance, NameComposites display things like name
758
	 * relations, sec. references, etc. here.
759
	 *
760
	 * @param info
761
	 *            the text to display in the label
762
	 * @param append
763
	 *            whether the string should be appended to text that is already
764
	 *            shown in the label
765
	 */
766
	public void setNonEditableInfo(String info, boolean append) {
767
		// TODO non editable info should only be drawn once, when everything
768
		// else is drawn
769
		info = info.toUpperCase();
770

    
771
		if (append) {
772
			nonEditableText += ", " + info; //$NON-NLS-1$
773
			nonEditableHoverText += "\n" + info; //$NON-NLS-1$
774
		} else {
775
			nonEditableText = info;
776
			nonEditableHoverText = info;
777
		}
778

    
779
		if (nonEditableInfoLabel == null) {
780
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(
781
					control, ""); //$NON-NLS-1$
782
			TableWrapData layoutData = new TableWrapData(
783
					TableWrapData.FILL_GRAB, TableWrapData.TOP);
784
			// Set indent to viewer ruler's width
785
			if (getNameViewer().getRulerControl() != null) {
786
				// TODO right justify
787
				layoutData.indent = NameViewer.RULER_WIDTH;
788
			}
789
			nonEditableInfoLabel.setLayoutData(layoutData);
790

    
791
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
792
					nonEditableInfoLabel) {
793
				@Override
794
				public String getLabelText() {
795
					return nonEditableText.toUpperCase();
796
				}
797
			};
798
			nonEditableInfoLabel
799
					.addControlListener(nonEditableLabelEllipsisListener);
800

    
801
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
802
			nonEditableInfoHover.setRespectDisplayBounds(true);
803

    
804
		}
805
		nonEditableInfoHover.setText(nonEditableHoverText);
806
		nonEditableInfoLabel.setText(nonEditableText);
807

    
808
		calculateAnnotations();
809
	}
810

    
811
	@Override
812
    public T getData() {
813
		return data;
814
	}
815

    
816
	public void setData(T data) {
817
		this.data = HibernateProxyHelper.deproxy(data);
818
	}
819

    
820
	/**
821
	 * If the user hitting carriage return should cause something to happen -
822
	 * i.e. the creation of a new composite - call this method and override the
823
	 * method handleSplitText().
824
	 */
825
	protected void createLineBreakListener() {
826
		lineBreakListener = new LineBreakListener() {
827
			@Override
828
			public void handleSplitText(String text) {
829
				AbstractGroupedContainer.this.handleSplitText(text);
830
			}
831

    
832
		};
833

    
834
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
835

    
836
	}
837

    
838
	abstract class LabelEllipsisListener extends ControlAdapter {
839

    
840
		private final Label label;
841
		int width = 0;
842

    
843
		LabelEllipsisListener(Label label) {
844
			this.label = label;
845
		}
846

    
847
		abstract public String getLabelText();
848

    
849
		@Override
850
		public void controlResized(ControlEvent e) {
851
			if (label.getBounds().width == width) {
852
				return;
853
			}
854
			width = label.getBounds().width;
855
			if (label.getBounds().width > 0) {
856
				label.setText(TextHelper.shortenText(getLabelText(), label));
857
			}
858
		}
859
	}
860

    
861
	public void storeCursor() {
862
		this.cursorPosition = getNameViewer().getCursorPosition();
863
	}
864

    
865
	/**
866
	 * Puts the cursor to the position it was last seen on or to the end of line
867
	 * if no former position is known.
868
	 */
869
	public void placeCursor() {
870
		if (cursorPosition == 0) {
871
			getNameViewer().setCursorToEOL();
872
		} else {
873
			getNameViewer().setCursorPosition(cursorPosition);
874
		}
875
	}
876

    
877
	public void restoreColor() {
878
		setBackground(backgroundColor);
879
	}
880

    
881
	@Override
882
	public void initialize(IManagedForm form) {
883
		// TODO Auto-generated method stub
884
	}
885

    
886
	@Override
887
	public void dispose() {
888
		if (getControl() != null) {
889
			getControl().dispose();
890
		}
891
	}
892

    
893
	@Override
894
	public void commit(boolean onSave) {
895
		if (isDirty()) {
896
			persistName();
897
		}
898
	}
899

    
900
	@Override
901
	public boolean setFormInput(Object input) {
902
		return false;
903
	}
904

    
905
	@Override
906
	public void setFocus() {
907
		getNameViewer().getControl().setFocus();
908
	}
909

    
910
	@Override
911
	public boolean isStale() {
912
		return false;
913
	}
914

    
915
	public void setDisabled(boolean disabled) {
916
		setEnabled(!disabled);
917
	}
918

    
919
	public void setEnabled(boolean enabled) {
920
		Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
921

    
922
		getNameViewer().getTextWidget().setEditable(enabled);
923
		getNameViewer().getTextWidget().setForeground(color);
924
	}
925

    
926

    
927
}
(2-2/11)