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
package eu.etaxonomy.taxeditor.editor.name.e4.container;
10

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

    
15
import org.apache.commons.lang3.StringUtils;
16
import org.eclipse.core.runtime.Assert;
17
import org.eclipse.e4.ui.di.UISynchronize;
18
import org.eclipse.jface.dialogs.Dialog;
19
import org.eclipse.jface.text.IDocument;
20
import org.eclipse.jface.text.Position;
21
import org.eclipse.jface.window.DefaultToolTip;
22
import org.eclipse.swt.custom.StyledText;
23
import org.eclipse.swt.dnd.DND;
24
import org.eclipse.swt.dnd.DragSource;
25
import org.eclipse.swt.dnd.Transfer;
26
import org.eclipse.swt.events.ControlAdapter;
27
import org.eclipse.swt.events.ControlEvent;
28
import org.eclipse.swt.events.ControlListener;
29
import org.eclipse.swt.events.FocusAdapter;
30
import org.eclipse.swt.events.FocusEvent;
31
import org.eclipse.swt.events.FocusListener;
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.ui.forms.IFormPart;
44
import org.eclipse.ui.forms.IManagedForm;
45
import org.eclipse.ui.forms.widgets.TableWrapData;
46
import org.eclipse.ui.forms.widgets.TableWrapLayout;
47

    
48
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
49
import eu.etaxonomy.cdm.model.common.CdmBase;
50
import eu.etaxonomy.cdm.model.name.TaxonName;
51
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
52
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
53
import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
54
import eu.etaxonomy.taxeditor.editor.l10n.Messages;
55
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation;
56
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation.EditorAnnotationType;
57
import eu.etaxonomy.taxeditor.editor.name.container.IContainerConstants;
58
import eu.etaxonomy.taxeditor.editor.name.container.LineBreakListener;
59
import eu.etaxonomy.taxeditor.editor.name.container.LineWrapSupport;
60
import eu.etaxonomy.taxeditor.editor.name.e4.TaxonEditor;
61
import eu.etaxonomy.taxeditor.editor.name.e4.dnd.NameEditorDragListenerE4;
62
import eu.etaxonomy.taxeditor.editor.name.e4.dnd.NameEditorDragSourceEffect;
63
import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
64
import eu.etaxonomy.taxeditor.model.AbstractUtility;
65
import eu.etaxonomy.taxeditor.model.IElementHasDetails;
66
import eu.etaxonomy.taxeditor.model.NameHelper;
67
import eu.etaxonomy.taxeditor.model.TextHelper;
68
import eu.etaxonomy.taxeditor.parser.ParseHandler;
69
import eu.etaxonomy.taxeditor.preference.Resources;
70

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

    
97
	protected ParseHandler parseHandler;
98

    
99
	private FocusListener nameCompositeFocusListener;
100
	private ModifyListener nameCompositeModifyListener;
101

    
102
	protected NameViewer nameViewer;
103

    
104
	private AbstractGroup group;
105

    
106
	private Label nonEditableInfoLabel;
107
	private DefaultToolTip nonEditableInfoHover;
108

    
109
	private static AbstractGroupedContainer<?> selection;
110

    
111
	private FocusListener focusListener;
112
	private LineBreakListener lineBreakListener;
113

    
114
	private int cursorPosition;
115

    
116
	private long lastEventTime;
117

    
118
	protected Composite control;
119

    
120
	private Color backgroundColor;
121
	private boolean isDirty;
122

    
123
	public AbstractGroupedContainer(AbstractGroup group, T taxonBase) {
124
		setData(taxonBase);
125
		this.group = group;
126
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
127
	}
128

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

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

    
136
		setMenu();
137

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

    
141
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
142

    
143
		initializeComposite();
144

    
145
		createListener();
146

    
147
		enableFreeText();
148
	}
149

    
150
	protected void createListener() {
151
		nameCompositeModifyListener = (ModifyEvent e)->{
152
				// mark the composite dirty
153
				setDirty(true);
154
				lastEventTime = System.currentTimeMillis();
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

    
163
				// show errors resulting from parsing
164
				calculateAnnotations();
165
				// store the position of the cursor
166
				storeCursor();
167
				// notify selection listener
168
				setDelayedSelection();
169
				//EventUtility.postAsyncEvent(WorkbenchEventConstants.REFRESH_DETAILS, true);
170

    
171
		};
172

    
173
		nameCompositeFocusListener = new FocusAdapter() {
174

    
175
			@Override
176
			public void focusLost(FocusEvent e) {
177
				super.focusLost(e);
178
				persistName();
179
//				EventUtility.postAsyncEvent(WorkbenchEventConstants.REFRESH_DETAILS, true);
180
			}
181
		};
182

    
183
		addListener();
184
	}
185

    
186
	protected void addListener() {
187
		getNameViewer().getTextWidget().addModifyListener(
188
				nameCompositeModifyListener);
189
		getNameViewer().getTextWidget().addFocusListener(
190
				nameCompositeFocusListener);
191
	}
192

    
193
	protected void removeListener() {
194
		getNameViewer().getTextWidget().removeModifyListener(
195
				nameCompositeModifyListener);
196
		getNameViewer().getTextWidget().removeFocusListener(
197
				nameCompositeFocusListener);
198
	}
199

    
200
	/**
201
	 * Initialize the composite specific code
202
	 */
203
	protected abstract void initializeComposite();
204

    
205
	protected String getEmptyTextPrompt() {
206
		return EMPTY_NAME_PROMPT;
207
	}
208

    
209
	protected void initTextViewer() {
210

    
211
		updateIndent();
212

    
213
		updateIcon();
214

    
215
		String text = NameHelper.getDisplayNameWithRef(getData());
216

    
217
		if (text.length() == 0) {
218
			initEmptyText();
219
		} else {
220
			getNameViewer().setText(text);
221
			placeCursor();
222
		}
223
		calculateAnnotations();
224
	}
225

    
226
	synchronized protected void calculateAnnotations() {
227
		getNameViewer().clearAnnotations();
228
		showAnnotations();
229
	}
230

    
231
	public void showAnnotations() {
232

    
233
		if (getName() != null && getName().hasProblem()) {
234
			showParsingProblems();
235
		}
236

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

    
245
		if (isNameUsedMultipleTimes()) {
246
			getNameViewer().addAnnotation(
247
					new EditorAnnotation(EditorAnnotationType.WARNING, 0,
248
							Messages.AbstractGroupedContainer_MULTIPLE_USE));
249
		}
250

    
251
	}
252

    
253
	private void showParsingProblems() {
254
		TaxonName name = getName();
255
		if (name == null){
256
			return;
257
		}
258

    
259
		List<ParserProblem> parsingProblems = name.getParsingProblems();
260

    
261
		for (ParserProblem problem : parsingProblems) {
262
			getNameViewer().addAnnotation(new EditorAnnotation(problem),
263
					getParsingProblemPosition());
264
		}
265
	}
266

    
267
	private Position getParsingProblemPosition() {
268
		String text = getNameViewer().getTextWidget().getText();
269

    
270
		if (getName() != null && getName().hasProblem() && text.length() > 0) {
271
			int start = getName().getProblemStarts();
272
			int length = getName().getProblemEnds() - start;
273

    
274
			if (start == -1 || getName().getProblemEnds() == -1) {
275
				return null;
276
			}
277

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

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

    
295
		AbstractUtility.executeOperation(new CreateSynonymInNewGroupOperation(
296
				Messages.AbstractGroupedContainer_NEW_HETERO_SYNONYM, getEditor().getUndoContext(),
297
				getEditor().getTaxon(), synonymName, getEditor()), group.getContext().get(UISynchronize.class));
298
	}
299

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

    
309
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
310

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

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

    
324
		updateNonEditableInfo();
325

    
326
		updateIcon();
327
		// placeCursor();
328
		updateIndent();
329
		setDelayedSelection();
330
		enableFreeText();
331
	}
332

    
333
	protected abstract void updateIcon();
334

    
335
	protected abstract void updateIndent();
336

    
337
	protected abstract void updateNonEditableInfo();
338

    
339
	protected void enableFreeText() {
340
		setEnabled(isFreetextEditingAllowed());
341
	}
342

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

    
352
		enableFreetext |= isNameUsedMultipleTimes();
353
		enableFreetext &= isNameParsable();
354

    
355
		return enableFreetext;
356
	}
357

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

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

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

    
385
	private boolean isNameParsable() {
386
		TaxonName name = getName();
387
		if (name == null){
388
			return false;
389
		}
390

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

    
400
		return isParsable;
401
	}
402

    
403
    private boolean nameAuthorsSet(TaxonName name) {
404
       boolean result = true;
405
       result &= name.getBasionymAuthorship() == null && name.getCombinationAuthorship() == null && name.getExBasionymAuthorship() == null && name.getExCombinationAuthorship() == null;
406
       return result;
407
    }
408

    
409
    private boolean nameEpithetsNotSet(TaxonName name) {
410
        boolean result = true;
411
        result &= name.getGenusOrUninomial() == null && name.getInfraGenericEpithet() == null && name.getSpecificEpithet() == null && name.getInfraSpecificEpithet() == null;
412

    
413
        return result;
414
    }
415

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

    
427
	public T getTaxonBase() {
428
		return getData();
429
	}
430

    
431
	public TaxonName getName() {
432
		return CdmBase.deproxy(getTaxonBase().getName());
433
	}
434

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

    
449
	public AbstractGroup getGroup() {
450
		if (group == null) {
451
			throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
452
		}
453
		return group;
454
	}
455

    
456
	public void remove() {
457
		getGroup().remove(this);
458
	}
459

    
460
	protected void createControl() {
461
		control = getEditor().getToolkit().createComposite(
462
				getGroup().getControl());
463

    
464
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
465
		TableWrapLayout layout = new TableWrapLayout();
466
		layout.leftMargin = 0;
467
		layout.rightMargin = 0;
468
		layout.topMargin = 5;
469
		layout.bottomMargin = 5;
470

    
471
		layout.verticalSpacing = 0;
472
		layout.horizontalSpacing = 0;
473

    
474
		control.setLayout(layout);
475

    
476
	}
477

    
478
	protected TaxonEditor getEditor() {
479
		return getGroup().getEditor();
480
	}
481

    
482
	public Composite getControl() {
483
		return control;
484
	}
485

    
486
	protected void createLineWrapSupport() {
487
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
488
	}
489

    
490
	protected void createTextViewer() {
491
		nameViewer = new NameViewer(control);
492

    
493
		focusListener = new FocusAdapter() {
494
			@Override
495
			public void focusGained(FocusEvent e) {
496
			    if (getEditor()
497
                        .getGroupedContainers().isEmpty()){
498
			        return;
499
			    }
500

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

    
513
		MouseAdapter mouseListener = new MouseAdapter() {
514
			@Override
515
			public void mouseDown(MouseEvent e) {
516
				storeCursor();
517
			}
518
		};
519
		control.addMouseListener(mouseListener);
520
		nameViewer.getRulerControl().addMouseListener(mouseListener);
521
		nameViewer.getTextWidget().addMouseListener(mouseListener);
522
	}
523

    
524
	public void setIcon(Image icon) {
525
		getNameViewer().setIcon(icon);
526
	}
527

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

    
541
	public void setSelected() {
542
		getNameViewer().getTextWidget().setFocus();
543
	}
544

    
545
	public boolean isSelected() {
546
		return getEditor().getSelectedContainer() == this;
547
	}
548

    
549
	public void colorSelected(int mode) {
550
		if (control!=null && !control.isDisposed()) {
551
			String colorString = null;
552

    
553
			switch (mode) {
554
			case SELECTED_FOCUS:
555
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
556
				break;
557
			case SELECTED_NO_FOCUS:
558
				colorString = Resources.COLOR_CONTROL_SELECTED;
559
				break;
560
			default:
561
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
562
			}
563

    
564
			backgroundColor = AbstractUtility.getColor(colorString);
565

    
566
			setBackground(backgroundColor);
567
		}
568
	}
569

    
570
	private Delay delay = new Delay(Display.getCurrent());
571

    
572
	private class Delay extends Thread{
573

    
574
	    private volatile boolean stop = false;
575
        private Display display;
576

    
577
        private Delay(Display display) {
578
            this.display = display;
579
        }
580
        @Override
581
        public void run() {
582

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

    
603
	protected void setDelayedSelection() {
604
		selection = this;
605

    
606
		Display display = Display.getCurrent();
607

    
608
		delay.requestStop();
609
		delay = new Delay(display);
610
		delay.start();
611
	}
612

    
613
	public void setBackground(Color color) {
614
		control.setBackground(color);
615

    
616
		for (Control child : control.getChildren()) {
617
			child.setBackground(color);
618
		}
619

    
620
		getNameViewer().setBackground(color);
621
	}
622

    
623
	public void setFont(Font font) {
624
		getNameViewer().getTextWidget().setFont(font);
625
	}
626

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

    
636
	public void createEmptyViewerPrompt(final String prompt) {
637

    
638
		Assert.isNotNull(getNameViewer());
639

    
640
		final StyledText textControl = getNameViewer().getTextWidget();
641
		final IDocument document = getNameViewer().getDocument();
642

    
643
		setFocusListener(new FocusListener() {
644

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

    
653
			@Override
654
            public void focusLost(FocusEvent e) {
655
				if (document.getLength() == 0) {
656
					initEmptyText();
657
				}
658
			}
659

    
660
		});
661
		textControl.addFocusListener(getFocusListener());
662

    
663
		if (document.getLength() == 0) {
664
			textControl.setFont(AbstractUtility
665
					.getFont(Resources.FONT_DEFAULT_PROMPT));
666
			document.set(prompt);
667
		}
668
	}
669

    
670
	abstract protected Font getViewerFont();
671

    
672
	protected void initEmptyText() {
673
		Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
674
		getNameViewer().getTextWidget().setFont(defaultFont);
675

    
676
		getNameViewer().getDocument().set(getEmptyTextPrompt());
677
	}
678

    
679
	protected void setFocusListener(FocusListener focusListener) {
680
		this.focusListener = focusListener;
681
	}
682

    
683
	private FocusListener getFocusListener() {
684
		return focusListener;
685
	}
686

    
687
	public void setDirty(boolean isDirty) {
688
		if (isDirty) {
689
			getEditor().getManagedForm().dirtyStateChanged();
690
		}
691
		this.isDirty = isDirty;
692
	}
693

    
694
	@Override
695
    public boolean isDirty() {
696
		return isDirty;
697
	}
698

    
699
	public void setMenu() {
700
        getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
701
	}
702

    
703
	private Control[] draggableControls;
704

    
705
	protected void setDraggableControl(Control[] controls) {
706
		draggableControls = controls;
707
	}
708

    
709
	public void setIsDraggable(boolean draggable) {
710

    
711
		if (draggable) {
712

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

    
718
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
719
			int operations = DND.DROP_MOVE;
720

    
721
			for (Control draggableControl : draggableControls) {
722
				DragSource dragSource = new DragSource(draggableControl,
723
						operations);
724
				dragSource.setTransfer(types);
725

    
726
				dragSource.addDragListener(new NameEditorDragListenerE4(this));
727
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
728
						control));
729
			}
730
		}
731
	}
732

    
733
	private String nonEditableText;
734

    
735
	ControlListener nonEditableResizeListener = new ControlAdapter() {
736

    
737
		int width = 0;
738

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

    
752
	private String nonEditableHoverText;
753

    
754
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
755

    
756
	private T data;
757

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

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

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

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

    
804
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
805
			nonEditableInfoHover.setRespectDisplayBounds(true);
806

    
807
		}
808
		nonEditableInfoHover.setText(nonEditableHoverText);
809
		nonEditableInfoLabel.setText(nonEditableText);
810

    
811
		calculateAnnotations();
812
	}
813

    
814
	@Override
815
    public T getData() {
816
		return data;
817
	}
818

    
819
	public void setData(T data) {
820
		this.data = HibernateProxyHelper.deproxy(data);
821
	}
822

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

    
835
		};
836

    
837
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
838

    
839
	}
840

    
841
	abstract class LabelEllipsisListener extends ControlAdapter {
842

    
843
		private final Label label;
844
		int width = 0;
845

    
846
		LabelEllipsisListener(Label label) {
847
			this.label = label;
848
		}
849

    
850
		abstract public String getLabelText();
851

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

    
864
	public void storeCursor() {
865
		this.cursorPosition = getNameViewer().getCursorPosition();
866
	}
867

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

    
880
	public void restoreColor() {
881
		setBackground(backgroundColor);
882
	}
883

    
884
	@Override
885
	public void initialize(IManagedForm form) {
886
		// TODO Auto-generated method stub
887
	}
888

    
889
	@Override
890
	public void dispose() {
891
		if (getControl() != null) {
892
			getControl().dispose();
893
		}
894
	}
895

    
896
	@Override
897
	public void commit(boolean onSave) {
898
		if (isDirty()) {
899
			persistName();
900
		}
901
	}
902

    
903
	@Override
904
	public boolean setFormInput(Object input) {
905
		return false;
906
	}
907

    
908
	@Override
909
	public void setFocus() {
910
		getNameViewer().getControl().setFocus();
911
	}
912

    
913
	@Override
914
	public boolean isStale() {
915
		return false;
916
	}
917

    
918
	public void setDisabled(boolean disabled) {
919
		setEnabled(!disabled);
920
	}
921

    
922
	public void setEnabled(boolean enabled) {
923
		Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
924

    
925
		getNameViewer().getTextWidget().setEditable(enabled);
926
		getNameViewer().getTextWidget().setForeground(color);
927
	}
928

    
929

    
930
}
(2-2/11)