Project

General

Profile

Download (24.2 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.lang.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.TaxonNameEditorE4;
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 AbstractGroupedContainerE4<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 NameViewerE4 nameViewer;
104

    
105
	private AbstractGroupE4 group;
106

    
107
	private Label nonEditableInfoLabel;
108
	private DefaultToolTip nonEditableInfoHover;
109

    
110
	private static AbstractGroupedContainerE4<?> 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
//    private ISelectionChangedListener selectionChangedListener;
123

    
124
	public AbstractGroupedContainerE4(AbstractGroupE4 group, T taxonBase) {
125
		setData(taxonBase);
126
		this.group = group;
127
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
128
	}
129

    
130
	public void createContent() {
131
		createControl();
132

    
133
		createTextViewer();
134
		createLineWrapSupport();
135
		createLineBreakListener();
136

    
137
		setMenu();
138

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

    
142
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
143

    
144
		initializeComposite();
145

    
146
		createListener();
147

    
148
		enableFreeText();
149
	}
150

    
151
	protected void createListener() {
152
		nameCompositeModifyListener = (ModifyEvent e)->{
153
				// mark the composite dirty
154
				setDirty(true);
155
				// parse the text
156
				String text = nameViewer.getTextWidget().getText();
157

    
158
				TaxonName name = (TaxonName)parseHandler.parse(text);
159
				getTaxonBase().setName(name);
160
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
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

    
176
				persistName();
177
			}
178
		};
179

    
180
		addListener();
181
	}
182

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

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

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

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

    
206
	protected void initTextViewer() {
207

    
208
		updateIndent();
209

    
210
		updateIcon();
211

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

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

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

    
228
	public void showAnnotations() {
229

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

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

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

    
248
	}
249

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

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

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

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

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

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

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

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

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

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

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

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

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

    
321
		updateNonEditableInfo();
322

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

    
330
	protected abstract void updateIcon();
331

    
332
	protected abstract void updateIndent();
333

    
334
	protected abstract void updateNonEditableInfo();
335

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

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

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

    
352
		return enableFreetext;
353
	}
354

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

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

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

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

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

    
397
		return isParsable;
398
	}
399

    
400
	/**
401
     * @param name
402
     * @return
403
     */
404
    private boolean nameAuthorsSet(TaxonName name) {
405
       boolean result = true;
406
       result &= name.getBasionymAuthorship() == null && name.getCombinationAuthorship() == null && name.getExBasionymAuthorship() == null && name.getExCombinationAuthorship() == null;
407
       return result;
408
    }
409

    
410
    /**
411
     * @param name
412
     * @return
413
     */
414
    private boolean nameEpithetsNotSet(TaxonName name) {
415
        boolean result = true;
416
        result &= name.getGenusOrUninomial() == null && name.getInfraGenericEpithet() == null && name.getSpecificEpithet() == null && name.getInfraSpecificEpithet() == null;
417

    
418
        return result;
419
    }
420

    
421
    /**
422
	 * Parse the text and calculate errors
423
	 */
424
	public void parseAndCalculateAnnotations() {
425
		removeListener();
426
		String unparsedNameString = getNameViewer().getTextWidget().getText();
427
		parseHandler.parse(unparsedNameString);
428
		addListener();
429
		calculateAnnotations();
430
	}
431

    
432
	public T getTaxonBase() {
433
		return getData();
434
	}
435

    
436
	public TaxonName getName() {
437
		return CdmBase.deproxy(getTaxonBase().getName());
438
	}
439

    
440
	public void persistName() {
441
		if (isDirty()) {
442
			getNameViewer().getTextWidget().setEnabled(false);
443
			final String unparsedNameString = getNameViewer().getTextWidget()
444
					.getText();
445
			final TaxonName name = (TaxonName)parseHandler
446
					.parseAndResolveDuplicates(unparsedNameString);
447
			getTaxonBase().setName(name);
448
			getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
449
			setDirty(false);
450
			getNameViewer().getTextWidget().setEnabled(true);
451

    
452
		}
453
	}
454

    
455
	public AbstractGroupE4 getGroup() {
456
		if (group == null) {
457
			throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
458
		}
459
		return group;
460
	}
461

    
462
	public void remove() {
463
		getGroup().remove(this);
464
	}
465

    
466
	protected void createControl() {
467
		control = getEditor().getToolkit().createComposite(
468
				getGroup().getControl());
469

    
470
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
471
		TableWrapLayout layout = new TableWrapLayout();
472
		layout.leftMargin = 0;
473
		layout.rightMargin = 0;
474
		layout.topMargin = 5;
475
		layout.bottomMargin = 5;
476

    
477
		layout.verticalSpacing = 0;
478
		layout.horizontalSpacing = 0;
479

    
480
		control.setLayout(layout);
481

    
482
	}
483

    
484
	protected TaxonNameEditorE4 getEditor() {
485
		return getGroup().getEditor();
486
	}
487

    
488
	public Composite getControl() {
489
		return control;
490
	}
491

    
492
	protected void createLineWrapSupport() {
493
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
494
	}
495

    
496
	protected void createTextViewer() {
497
		nameViewer = new NameViewerE4(control);
498

    
499
		focusListener = new FocusAdapter() {
500
			@Override
501
			public void focusGained(FocusEvent e) {
502
			    if (getEditor()
503
                        .getGroupedContainers().isEmpty()){
504
			        return;
505
			    }
506

    
507
				for (AbstractGroupedContainerE4<?> container : getEditor()
508
						.getGroupedContainers()) {
509
					container.colorSelected(NOT_SELECTED);
510
				}
511
				getEditor().getManagedForm().setInput(
512
						AbstractGroupedContainerE4.this);
513
				placeCursor();
514
				colorSelected(SELECTED_FOCUS);
515
			}
516
		};
517
		nameViewer.getTextWidget().addFocusListener(focusListener);
518

    
519
		MouseAdapter mouseListener = new MouseAdapter() {
520
			@Override
521
			public void mouseDown(MouseEvent e) {
522
				storeCursor();
523
			}
524
		};
525
		control.addMouseListener(mouseListener);
526
		nameViewer.getRulerControl().addMouseListener(mouseListener);
527
		nameViewer.getTextWidget().addMouseListener(mouseListener);
528
	}
529

    
530
	public void setIcon(Image icon) {
531
		getNameViewer().setIcon(icon);
532
	}
533

    
534
	public void setIndent(int indent) {
535
		if (control.getLayout() instanceof TableWrapLayout) {
536
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
537
			layout.leftMargin = indent;
538
			layout.rightMargin = ACCEPTED_INDENT;
539
			control.setLayout(layout);
540
			control.layout();
541
		} else {
542
			throw new RuntimeException(
543
					"Couldn't indent - composite's layout must be TableWrapLayout."); //$NON-NLS-1$
544
		}
545
	}
546

    
547
	public void setSelected() {
548
		getNameViewer().getTextWidget().setFocus();
549
	}
550

    
551
	public boolean isSelected() {
552
		return getEditor().getSelectedContainer() == this;
553
	}
554

    
555
	public void colorSelected(int mode) {
556
		if (control!=null && !control.isDisposed()) {
557
			String colorString = null;
558

    
559
			switch (mode) {
560
			case SELECTED_FOCUS:
561
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
562
				break;
563
			case SELECTED_NO_FOCUS:
564
				colorString = Resources.COLOR_CONTROL_SELECTED;
565
				break;
566
			default:
567
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
568
			}
569

    
570
			backgroundColor = AbstractUtility.getColor(colorString);
571

    
572
			setBackground(backgroundColor);
573
		}else{
574
		    System.err.println("disposed");
575
		}
576
	}
577

    
578
	private Delay delay = new Delay(Display.getCurrent());
579

    
580
	private class Delay extends Thread{
581

    
582
	    private volatile boolean stop = false;
583
        private Display display;
584

    
585
        public Delay(Display display) {
586
            this.display = display;
587
        }
588
        @Override
589
        public void run() {
590

    
591
            while (!stop) {
592
                try {
593
                    // my code goes here
594
                    Thread.sleep(1000);
595
                    if(!stop){
596
                        display.syncExec(() -> {
597
                                getEditor().getManagedForm().setInput(selection);
598
                        });
599
                        stop = true;
600
                    }
601
                } catch (InterruptedException ex) {
602
                    stop = true;
603
                }
604
            }
605
        }
606
        public void requestStop() {
607
            stop = true;
608
        }
609
	}
610

    
611
	protected void setDelayedSelection() {
612
		selection = this;
613

    
614
		Display display = Display.getCurrent();
615

    
616
		delay.requestStop();
617
		delay = new Delay(display);
618
		delay.start();
619

    
620
	}
621

    
622
	public void setBackground(Color color) {
623
		control.setBackground(color);
624

    
625
		for (Control child : control.getChildren()) {
626
			child.setBackground(color);
627
		}
628

    
629
		getNameViewer().setBackground(color);
630
	}
631

    
632
	public void setFont(Font font) {
633
		getNameViewer().getTextWidget().setFont(font);
634
	}
635

    
636
	public NameViewerE4 getNameViewer() {
637
		if (nameViewer == null) {
638
			throw new RuntimeException(
639
					"The Name Viewer is corrupt for Name Container: " //$NON-NLS-1$
640
							+ getTaxonBase().getName().getTitleCache());
641
		}
642
		return nameViewer;
643
	}
644

    
645
	public void createEmptyViewerPrompt(final String prompt) {
646

    
647
		Assert.isNotNull(getNameViewer());
648

    
649
		final StyledText textControl = getNameViewer().getTextWidget();
650
		final IDocument document = getNameViewer().getDocument();
651

    
652
		setFocusListener(new FocusListener() {
653

    
654
			@Override
655
            public void focusGained(FocusEvent e) {
656
				if (document.get().equals(prompt)) {
657
					textControl.setFont(getViewerFont());
658
					document.set(""); //$NON-NLS-1$
659
				}
660
			}
661

    
662
			@Override
663
            public void focusLost(FocusEvent e) {
664
				if (document.getLength() == 0) {
665
					initEmptyText();
666
				}
667
			}
668

    
669
		});
670
		textControl.addFocusListener(getFocusListener());
671

    
672
		if (document.getLength() == 0) {
673
			textControl.setFont(AbstractUtility
674
					.getFont(Resources.FONT_DEFAULT_PROMPT));
675
			document.set(prompt);
676
		}
677
	}
678

    
679
	abstract protected Font getViewerFont();
680

    
681
	protected void initEmptyText() {
682
		Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
683
		getNameViewer().getTextWidget().setFont(defaultFont);
684

    
685
		getNameViewer().getDocument().set(getEmptyTextPrompt());
686
		placeCursor();
687
	}
688

    
689
	protected void setFocusListener(FocusListener focusListener) {
690
		this.focusListener = focusListener;
691
	}
692

    
693
	private FocusListener getFocusListener() {
694
		return focusListener;
695
	}
696

    
697
	public void setDirty(boolean isDirty) {
698
		if (isDirty) {
699
			getEditor().getManagedForm().dirtyStateChanged();
700
		}
701
		this.isDirty = isDirty;
702
	}
703

    
704
	@Override
705
    public boolean isDirty() {
706
		return isDirty;
707
	}
708

    
709
	public void setMenu() {
710
        getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
711
	}
712

    
713
	private Control[] draggableControls;
714

    
715
	protected void setDraggableControl(Control[] controls) {
716
		draggableControls = controls;
717
	}
718

    
719
	public void setIsDraggable(boolean draggable) {
720

    
721
		if (draggable) {
722

    
723
			if (draggableControls == null) {
724
				throw new NullPointerException(
725
						"Draggable controls must be set to add draggability"); //$NON-NLS-1$
726
			}
727

    
728
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
729
			int operations = DND.DROP_MOVE;
730

    
731
			for (Control draggableControl : draggableControls) {
732
				DragSource dragSource = new DragSource(draggableControl,
733
						operations);
734
				dragSource.setTransfer(types);
735

    
736
				dragSource.addDragListener(new NameEditorDragListenerE4(this));
737
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
738
						control));
739
			}
740
		}
741
	}
742

    
743
	private String nonEditableText;
744

    
745
	ControlListener nonEditableResizeListener = new ControlAdapter() {
746

    
747
		int width = 0;
748

    
749
		@Override
750
		public void controlResized(ControlEvent e) {
751
			if (nonEditableInfoLabel.getBounds().width == width) {
752
				return;
753
			}
754
			width = nonEditableInfoLabel.getBounds().width;
755
			if (nonEditableInfoLabel.getBounds().width > 0) {
756
				nonEditableInfoLabel.setText(Dialog.shortenText(
757
						nonEditableText.toUpperCase(), nonEditableInfoLabel));
758
			}
759
		}
760
	};
761

    
762
	private String nonEditableHoverText;
763

    
764
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
765

    
766
	private T data;
767

    
768
	/**
769
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
770
	 * input field. For instance, NameComposites display things like name
771
	 * relations, sec. references, etc. here.
772
	 *
773
	 * @param info
774
	 *            the text to display in the label
775
	 * @param append
776
	 *            whether the string should be appended to text that is already
777
	 *            shown in the label
778
	 */
779
	public void setNonEditableInfo(String info, boolean append) {
780
		// TODO non editable info should only be drawn once, when everything
781
		// else is drawn
782
		info = info.toUpperCase();
783

    
784
		if (append) {
785
			nonEditableText += ", " + info; //$NON-NLS-1$
786
			nonEditableHoverText += "\n" + info; //$NON-NLS-1$
787
		} else {
788
			nonEditableText = info;
789
			nonEditableHoverText = info;
790
		}
791

    
792
		if (nonEditableInfoLabel == null) {
793
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(
794
					control, ""); //$NON-NLS-1$
795
			TableWrapData layoutData = new TableWrapData(
796
					TableWrapData.FILL_GRAB, TableWrapData.TOP);
797
			// Set indent to viewer ruler's width
798
			if (getNameViewer().getRulerControl() != null) {
799
				// TODO right justify
800
				layoutData.indent = NameViewerE4.RULER_WIDTH;
801
			}
802
			nonEditableInfoLabel.setLayoutData(layoutData);
803

    
804
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
805
					nonEditableInfoLabel) {
806
				@Override
807
				public String getLabelText() {
808
					return nonEditableText.toUpperCase();
809
				}
810
			};
811
			nonEditableInfoLabel
812
					.addControlListener(nonEditableLabelEllipsisListener);
813

    
814
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
815
			nonEditableInfoHover.setRespectDisplayBounds(true);
816

    
817
		}
818
		nonEditableInfoHover.setText(nonEditableHoverText);
819
		nonEditableInfoLabel.setText(nonEditableText);
820

    
821
		calculateAnnotations();
822
	}
823

    
824
	@Override
825
    public T getData() {
826
		return data;
827
	}
828

    
829
	public void setData(T data) {
830
		this.data = HibernateProxyHelper.deproxy(data);
831
	}
832

    
833
	/**
834
	 * If the user hitting carriage return should cause something to happen -
835
	 * i.e. the creation of a new composite - call this method and override the
836
	 * method handleSplitText().
837
	 */
838
	protected void createLineBreakListener() {
839
		lineBreakListener = new LineBreakListener() {
840
			@Override
841
			public void handleSplitText(String text) {
842
				AbstractGroupedContainerE4.this.handleSplitText(text);
843
			}
844

    
845
		};
846

    
847
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
848

    
849
	}
850

    
851
	abstract class LabelEllipsisListener extends ControlAdapter {
852

    
853
		private final Label label;
854
		int width = 0;
855

    
856
		LabelEllipsisListener(Label label) {
857
			this.label = label;
858
		}
859

    
860
		abstract public String getLabelText();
861

    
862
		@Override
863
		public void controlResized(ControlEvent e) {
864
			if (label.getBounds().width == width) {
865
				return;
866
			}
867
			width = label.getBounds().width;
868
			if (label.getBounds().width > 0) {
869
				label.setText(TextHelper.shortenText(getLabelText(), label));
870
			}
871
		}
872
	}
873

    
874
	public void storeCursor() {
875
		this.cursorPosition = getNameViewer().getCursorPosition();
876
	}
877

    
878
	/**
879
	 * Puts the cursor to the position it was last seen on or to the end of line
880
	 * if no former position is known.
881
	 */
882
	public void placeCursor() {
883
		if (cursorPosition == 0) {
884
			getNameViewer().setCursorToEOL();
885
		} else {
886
			getNameViewer().setCursorPosition(cursorPosition);
887
		}
888
	}
889

    
890
	public void restoreColor() {
891
		setBackground(backgroundColor);
892
	}
893

    
894
	@Override
895
	public void initialize(IManagedForm form) {
896
		// TODO Auto-generated method stub
897

    
898
	}
899

    
900
	@Override
901
	public void dispose() {
902
		if (getControl() != null) {
903

    
904
			getControl().dispose();
905
		}
906
	}
907

    
908
	@Override
909
	public void commit(boolean onSave) {
910
		if (isDirty()) {
911
			persistName();
912
		}
913
	}
914

    
915
	@Override
916
	public boolean setFormInput(Object input) {
917
		return false;
918
	}
919

    
920
	@Override
921
	public void setFocus() {
922
		getNameViewer().getControl().setFocus();
923
	}
924

    
925
	@Override
926
	public boolean isStale() {
927
		return false;
928
	}
929

    
930
	public void setDisabled(boolean disabled) {
931
		setEnabled(!disabled);
932
	}
933

    
934
	public void setEnabled(boolean enabled) {
935
		Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
936

    
937
		getNameViewer().getTextWidget().setEditable(enabled);
938
		getNameViewer().getTextWidget().setForeground(color);
939
	}
940

    
941

    
942
}
(2-2/11)