Project

General

Profile

Download (24.3 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.viewers.ISelectionChangedListener;
23
import org.eclipse.jface.window.DefaultToolTip;
24
import org.eclipse.swt.custom.StyledText;
25
import org.eclipse.swt.dnd.DND;
26
import org.eclipse.swt.dnd.DragSource;
27
import org.eclipse.swt.dnd.Transfer;
28
import org.eclipse.swt.events.ControlAdapter;
29
import org.eclipse.swt.events.ControlEvent;
30
import org.eclipse.swt.events.ControlListener;
31
import org.eclipse.swt.events.FocusAdapter;
32
import org.eclipse.swt.events.FocusEvent;
33
import org.eclipse.swt.events.FocusListener;
34
import org.eclipse.swt.events.ModifyEvent;
35
import org.eclipse.swt.events.ModifyListener;
36
import org.eclipse.swt.events.MouseAdapter;
37
import org.eclipse.swt.events.MouseEvent;
38
import org.eclipse.swt.graphics.Color;
39
import org.eclipse.swt.graphics.Font;
40
import org.eclipse.swt.graphics.Image;
41
import org.eclipse.swt.widgets.Composite;
42
import org.eclipse.swt.widgets.Control;
43
import org.eclipse.swt.widgets.Display;
44
import org.eclipse.swt.widgets.Label;
45
import org.eclipse.ui.forms.IFormPart;
46
import org.eclipse.ui.forms.IManagedForm;
47
import org.eclipse.ui.forms.widgets.TableWrapData;
48
import org.eclipse.ui.forms.widgets.TableWrapLayout;
49

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

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

    
100
	protected ParseHandler parseHandler;
101

    
102
	private FocusListener nameCompositeFocusListener;
103
	private ModifyListener nameCompositeModifyListener;
104

    
105
	protected NameViewer nameViewer;
106

    
107
	private AbstractGroupE4 group;
108

    
109
	private Label nonEditableInfoLabel;
110
	private DefaultToolTip nonEditableInfoHover;
111

    
112
	private static AbstractGroupedContainerE4 selection;
113

    
114
	private FocusListener focusListener;
115
	private LineBreakListener lineBreakListener;
116

    
117
	private int cursorPosition;
118

    
119
	protected Composite control;
120

    
121
	private Color backgroundColor;
122
	private boolean isDirty;
123

    
124
    private ISelectionChangedListener selectionChangedListener;
125

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

    
132
	public void createContent() {
133
		createControl();
134

    
135
		createTextViewer();
136
		createLineWrapSupport();
137
		createLineBreakListener();
138

    
139
		setMenu();
140

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

    
144
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
145

    
146
		initializeComposite();
147

    
148
		createListener();
149

    
150
		enableFreeText();
151
	}
152

    
153
	protected void createListener() {
154
		nameCompositeModifyListener = new ModifyListener() {
155

    
156
			@Override
157
            public void modifyText(ModifyEvent e) {
158
				// mark the composite dirty
159
				setDirty(true);
160
				// parse the text
161
				String text = nameViewer.getTextWidget().getText();
162

    
163
				TaxonName name = (TaxonName)parseHandler.parse(text);
164
				getTaxonBase().setName(name);
165
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
166

    
167
				// show errors resulting from parsing
168
				calculateAnnotations();
169
				// store the position of the cursor
170
				storeCursor();
171
				// notify selection listener
172
				setDelayedSelection();
173
			}
174
		};
175
		nameCompositeFocusListener = new FocusAdapter() {
176

    
177
			@Override
178
			public void focusLost(FocusEvent e) {
179
				super.focusLost(e);
180

    
181
				persistName();
182
			}
183
		};
184

    
185
		addListener();
186
	}
187

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

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

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

    
207
	protected String getEmptyTextPrompt() {
208
		return EMPTY_NAME_PROMPT;
209
	}
210

    
211
	protected void initTextViewer() {
212

    
213
		updateIndent();
214

    
215
		updateIcon();
216

    
217
		String text = NameHelper.getDisplayNameWithRef(getData());
218

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

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

    
233
	public void showAnnotations() {
234

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

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

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

    
253
	}
254

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

    
261
		List<ParserProblem> parsingProblems = name.getParsingProblems();
262

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

    
269
	private Position getParsingProblemPosition() {
270
		String text = getNameViewer().getTextWidget().getText();
271

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

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

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

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

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

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

    
311
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
312

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

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

    
326
		updateNonEditableInfo();
327

    
328
		updateIcon();
329
		// placeCursor();
330
		updateIndent();
331

    
332
		enableFreeText();
333
	}
334

    
335
	protected abstract void updateIcon();
336

    
337
	protected abstract void updateIndent();
338

    
339
	protected abstract void updateNonEditableInfo();
340

    
341
	protected void enableFreeText() {
342
		setEnabled(isFreetextEditingAllowed());
343

    
344
	}
345

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

    
355
		enableFreetext |= isNameUsedMultipleTimes();
356
		enableFreetext &= isNameParsable();
357

    
358
		return enableFreetext;
359
	}
360

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

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

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

    
388
	private boolean isNameParsable() {
389
		TaxonName name = getName();
390
		if (name == null){
391
			return false;
392
		}
393

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

    
403
		return isParsable;
404
	}
405

    
406
	/**
407
     * @param name
408
     * @return
409
     */
410
    private boolean nameAuthorsSet(TaxonName name) {
411
       boolean result = true;
412
       result &= name.getBasionymAuthorship() == null && name.getCombinationAuthorship() == null && name.getExBasionymAuthorship() == null && name.getExCombinationAuthorship() == null;
413
       return result;
414
    }
415

    
416
    /**
417
     * @param name
418
     * @return
419
     */
420
    private boolean nameEpithetsNotSet(TaxonName name) {
421
        boolean result = true;
422
        result &= name.getGenusOrUninomial() == null && name.getInfraGenericEpithet() == null && name.getSpecificEpithet() == null && name.getInfraSpecificEpithet() == null;
423

    
424
        return result;
425
    }
426

    
427
    /**
428
	 * Parse the text and calculate errors
429
	 */
430
	public void parseAndCalculateAnnotations() {
431
		removeListener();
432
		String unparsedNameString = getNameViewer().getTextWidget().getText();
433
		parseHandler.parse(unparsedNameString);
434
		addListener();
435
		calculateAnnotations();
436
	}
437

    
438
	public T getTaxonBase() {
439
		return getData();
440
	}
441

    
442
	public TaxonName getName() {
443
		return CdmBase.deproxy(getTaxonBase().getName());
444
	}
445

    
446
	public void persistName() {
447
		if (isDirty()) {
448
			getNameViewer().getTextWidget().setEnabled(false);
449
			final String unparsedNameString = getNameViewer().getTextWidget()
450
					.getText();
451
			final TaxonName name = (TaxonName)parseHandler
452
					.parseAndResolveDuplicates(unparsedNameString);
453
			getTaxonBase().setName(name);
454
			getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
455
			setDirty(false);
456
			getNameViewer().getTextWidget().setEnabled(true);
457
		}
458
	}
459

    
460
	public AbstractGroupE4 getGroup() {
461
		if (group == null) {
462
			throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
463
		}
464
		return group;
465
	}
466

    
467
	public void remove() {
468
		getGroup().remove(this);
469
	}
470

    
471
	protected void createControl() {
472
		control = getEditor().getToolkit().createComposite(
473
				getGroup().getControl());
474

    
475
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
476
		TableWrapLayout layout = new TableWrapLayout();
477
		layout.leftMargin = 0;
478
		layout.rightMargin = 0;
479
		layout.topMargin = 5;
480
		layout.bottomMargin = 5;
481

    
482
		layout.verticalSpacing = 0;
483
		layout.horizontalSpacing = 0;
484

    
485
		control.setLayout(layout);
486

    
487
	}
488

    
489
	protected TaxonNameEditorE4 getEditor() {
490
		return getGroup().getEditor();
491
	}
492

    
493
	public Composite getControl() {
494
		return control;
495
	}
496

    
497
	protected void createLineWrapSupport() {
498
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
499
	}
500

    
501
	protected void createTextViewer() {
502
		nameViewer = new NameViewer(control);
503

    
504
		focusListener = new FocusAdapter() {
505
			@Override
506
			public void focusGained(FocusEvent e) {
507
			    if (getEditor()
508
                        .getGroupedContainers().isEmpty()){
509
			        return;
510
			    }
511

    
512
				for (AbstractGroupedContainerE4<?> container : getEditor()
513
						.getGroupedContainers()) {
514
					container.colorSelected(NOT_SELECTED);
515
				}
516
				getEditor().getManagedForm().setInput(
517
						AbstractGroupedContainerE4.this);
518
				placeCursor();
519
				colorSelected(SELECTED_FOCUS);
520
			}
521
		};
522
		nameViewer.getTextWidget().addFocusListener(focusListener);
523

    
524
		MouseAdapter mouseListener = new MouseAdapter() {
525
			@Override
526
			public void mouseDown(MouseEvent e) {
527
				storeCursor();
528
			}
529
		};
530
		control.addMouseListener(mouseListener);
531
		nameViewer.getRulerControl().addMouseListener(mouseListener);
532
		nameViewer.getTextWidget().addMouseListener(mouseListener);
533
	}
534

    
535
	public void setIcon(Image icon) {
536
		getNameViewer().setIcon(icon);
537
	}
538

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

    
552
	public void setSelected() {
553
		getNameViewer().getTextWidget().setFocus();
554
	}
555

    
556
	public boolean isSelected() {
557
		return getEditor().getSelectedContainer() == this;
558
	}
559

    
560
	public void colorSelected(int mode) {
561
		if (!control.isDisposed()) {
562
			String colorString = null;
563

    
564
			switch (mode) {
565
			case SELECTED_FOCUS:
566
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
567
				break;
568
			case SELECTED_NO_FOCUS:
569
				colorString = Resources.COLOR_CONTROL_SELECTED;
570
				break;
571
			default:
572
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
573
			}
574

    
575
			backgroundColor = AbstractUtility.getColor(colorString);
576

    
577
			setBackground(backgroundColor);
578
		}
579
	}
580

    
581
	private Delay delay = new Delay(Display.getCurrent());
582

    
583
	private class Delay extends Thread{
584

    
585
	    private volatile boolean stop = false;
586
        private Display display;
587

    
588
        public Delay(Display display) {
589
            this.display = display;
590
        }
591
        @Override
592
        public void run() {
593

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

    
614
	protected void setDelayedSelection() {
615
		selection = this;
616

    
617
		Display display = Display.getCurrent();
618

    
619
		delay.requestStop();
620
		delay = new Delay(display);
621
		delay.start();
622

    
623
	}
624

    
625
	public void setBackground(Color color) {
626
		control.setBackground(color);
627

    
628
		for (Control child : control.getChildren()) {
629
			child.setBackground(color);
630
		}
631

    
632
		getNameViewer().setBackground(color);
633
	}
634

    
635
	public void setFont(Font font) {
636
		getNameViewer().getTextWidget().setFont(font);
637
	}
638

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

    
648
	public void createEmptyViewerPrompt(final String prompt) {
649

    
650
		Assert.isNotNull(getNameViewer());
651

    
652
		final StyledText textControl = getNameViewer().getTextWidget();
653
		final IDocument document = getNameViewer().getDocument();
654

    
655
		setFocusListener(new FocusListener() {
656

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

    
665
			@Override
666
            public void focusLost(FocusEvent e) {
667
				if (document.getLength() == 0) {
668
					initEmptyText();
669
				}
670
			}
671

    
672
		});
673
		textControl.addFocusListener(getFocusListener());
674

    
675
		if (document.getLength() == 0) {
676
			textControl.setFont(AbstractUtility
677
					.getFont(Resources.FONT_DEFAULT_PROMPT));
678
			document.set(prompt);
679
		}
680
	}
681

    
682
	abstract protected Font getViewerFont();
683

    
684
	protected void initEmptyText() {
685
		Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
686
		getNameViewer().getTextWidget().setFont(defaultFont);
687

    
688
		getNameViewer().getDocument().set(getEmptyTextPrompt());
689
		placeCursor();
690
	}
691

    
692
	protected void setFocusListener(FocusListener focusListener) {
693
		this.focusListener = focusListener;
694
	}
695

    
696
	private FocusListener getFocusListener() {
697
		return focusListener;
698
	}
699

    
700
	public void setDirty(boolean isDirty) {
701
		if (isDirty) {
702
			getEditor().getManagedForm().dirtyStateChanged();
703
		}
704
		this.isDirty = isDirty;
705
	}
706

    
707
	@Override
708
    public boolean isDirty() {
709
		return isDirty;
710
	}
711

    
712
	public void setMenu() {
713
        getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
714
	}
715

    
716
	private Control[] draggableControls;
717

    
718
	protected void setDraggableControl(Control[] controls) {
719
		draggableControls = controls;
720
	}
721

    
722
	public void setIsDraggable(boolean draggable) {
723

    
724
		if (draggable) {
725

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

    
731
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
732
			int operations = DND.DROP_MOVE;
733

    
734
			for (Control draggableControl : draggableControls) {
735
				DragSource dragSource = new DragSource(draggableControl,
736
						operations);
737
				dragSource.setTransfer(types);
738

    
739
				dragSource.addDragListener(new NameEditorDragListenerE4(this));
740
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
741
						control));
742
			}
743
		}
744
	}
745

    
746
	private String nonEditableText;
747

    
748
	ControlListener nonEditableResizeListener = new ControlAdapter() {
749

    
750
		int width = 0;
751

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

    
765
	private String nonEditableHoverText;
766

    
767
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
768

    
769
	private T data;
770

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

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

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

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

    
817
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
818
			nonEditableInfoHover.setRespectDisplayBounds(true);
819

    
820
		}
821
		nonEditableInfoHover.setText(nonEditableHoverText);
822
		nonEditableInfoLabel.setText(nonEditableText);
823

    
824
		calculateAnnotations();
825
	}
826

    
827
	@Override
828
    public T getData() {
829
		return data;
830
	}
831

    
832
	public void setData(T data) {
833
		this.data = HibernateProxyHelper.deproxy(data);
834
	}
835

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

    
848
		};
849

    
850
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
851

    
852
	}
853

    
854
	abstract class LabelEllipsisListener extends ControlAdapter {
855

    
856
		private final Label label;
857
		int width = 0;
858

    
859
		LabelEllipsisListener(Label label) {
860
			this.label = label;
861
		}
862

    
863
		abstract public String getLabelText();
864

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

    
877
	public void storeCursor() {
878
		this.cursorPosition = getNameViewer().getCursorPosition();
879
	}
880

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

    
893
	public void restoreColor() {
894
		setBackground(backgroundColor);
895
	}
896

    
897
	@Override
898
	public void initialize(IManagedForm form) {
899
		// TODO Auto-generated method stub
900

    
901
	}
902

    
903
	@Override
904
	public void dispose() {
905
		if (getControl() != null) {
906
			setMenu();
907
			getControl().dispose();
908
		}
909
	}
910

    
911
	@Override
912
	public void commit(boolean onSave) {
913
		if (isDirty()) {
914
			persistName();
915
		}
916
	}
917

    
918
	@Override
919
	public boolean setFormInput(Object input) {
920
		return false;
921
	}
922

    
923
	@Override
924
	public void setFocus() {
925
		getNameViewer().getControl().setFocus();
926
	}
927

    
928
	@Override
929
	public boolean isStale() {
930
		return false;
931
	}
932

    
933
	public void setDisabled(boolean disabled) {
934
		setEnabled(!disabled);
935
	}
936

    
937
	public void setEnabled(boolean enabled) {
938
		Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
939

    
940
		getNameViewer().getTextWidget().setEditable(enabled);
941
		getNameViewer().getTextWidget().setForeground(color);
942
	}
943

    
944

    
945
}
(2-2/11)