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
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.event.EventUtility;
65
import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
66
import eu.etaxonomy.taxeditor.model.AbstractUtility;
67
import eu.etaxonomy.taxeditor.model.IElementHasDetails;
68
import eu.etaxonomy.taxeditor.model.NameHelper;
69
import eu.etaxonomy.taxeditor.model.TextHelper;
70
import eu.etaxonomy.taxeditor.parser.ParseHandler;
71
import eu.etaxonomy.taxeditor.preference.Resources;
72

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

    
99
	protected ParseHandler parseHandler;
100

    
101
	private FocusListener nameCompositeFocusListener;
102
	private ModifyListener nameCompositeModifyListener;
103

    
104
	protected NameViewer nameViewer;
105

    
106
	private AbstractGroup group;
107

    
108
	private Label nonEditableInfoLabel;
109
	private DefaultToolTip nonEditableInfoHover;
110

    
111
	private static AbstractGroupedContainer<?> selection;
112

    
113
	private FocusListener focusListener;
114
	private LineBreakListener lineBreakListener;
115

    
116
	private int cursorPosition;
117

    
118
	private long lastEventTime;
119

    
120
	protected Composite control;
121

    
122
	private Color backgroundColor;
123
	private boolean isDirty;
124

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

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

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

    
138
		setMenu();
139

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

    
143
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
144

    
145
		initializeComposite();
146

    
147
		createListener();
148

    
149
		enableFreeText();
150
	}
151

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

    
160
				TaxonName name = (TaxonName)parseHandler.parse(text);
161
				getTaxonBase().setName(name);
162
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
163

    
164

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

    
173
		};
174

    
175
		nameCompositeFocusListener = new FocusAdapter() {
176

    
177
			@Override
178
			public void focusLost(FocusEvent e) {
179
				super.focusLost(e);
180
				persistName();
181
				EventUtility.postAsyncEvent(WorkbenchEventConstants.REFRESH_DETAILS, true);
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
		setDelayedSelection();
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
	 * Checks whether the freetext should be editable based on specific empty
347
	 * fields.
348
	 *
349
	 * @return
350
	 */
351
	private boolean isFreetextEditingAllowed() {
352
		boolean enableFreetext = true;
353

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

    
357
		return enableFreetext;
358
	}
359

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

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

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

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

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

    
402
		return isParsable;
403
	}
404

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

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

    
415
        return result;
416
    }
417

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

    
429
	public T getTaxonBase() {
430
		return getData();
431
	}
432

    
433
	public TaxonName getName() {
434
		return CdmBase.deproxy(getTaxonBase().getName());
435
	}
436

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

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

    
458
	public void remove() {
459
		getGroup().remove(this);
460
	}
461

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

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

    
473
		layout.verticalSpacing = 0;
474
		layout.horizontalSpacing = 0;
475

    
476
		control.setLayout(layout);
477

    
478
	}
479

    
480
	protected TaxonEditor getEditor() {
481
		return getGroup().getEditor();
482
	}
483

    
484
	public Composite getControl() {
485
		return control;
486
	}
487

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

    
492
	protected void createTextViewer() {
493
		nameViewer = new NameViewer(control);
494

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

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

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

    
526
	public void setIcon(Image icon) {
527
		getNameViewer().setIcon(icon);
528
	}
529

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

    
543
	public void setSelected() {
544
		getNameViewer().getTextWidget().setFocus();
545
	}
546

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

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

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

    
566
			backgroundColor = AbstractUtility.getColor(colorString);
567

    
568
			setBackground(backgroundColor);
569
		}
570
	}
571

    
572
	private Delay delay = new Delay(Display.getCurrent());
573

    
574
	private class Delay extends Thread{
575

    
576
	    private volatile boolean stop = false;
577
        private Display display;
578

    
579
        private Delay(Display display) {
580
            this.display = display;
581
        }
582

    
583
        @Override
584
        public void run() {
585

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

    
606
	protected void setDelayedSelection() {
607
		selection = this;
608

    
609
		Display display = Display.getCurrent();
610

    
611
		delay.requestStop();
612
		delay = new Delay(display);
613
		delay.start();
614
	}
615

    
616
	public void setBackground(Color color) {
617
		control.setBackground(color);
618

    
619
		for (Control child : control.getChildren()) {
620
			child.setBackground(color);
621
		}
622

    
623
		getNameViewer().setBackground(color);
624
	}
625

    
626
	public void setFont(Font font) {
627
		getNameViewer().getTextWidget().setFont(font);
628
	}
629

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

    
639
	public void createEmptyViewerPrompt(final String prompt) {
640

    
641
		Assert.isNotNull(getNameViewer());
642

    
643
		final StyledText textControl = getNameViewer().getTextWidget();
644
		final IDocument document = getNameViewer().getDocument();
645

    
646
		setFocusListener(new FocusListener() {
647

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

    
656
			@Override
657
            public void focusLost(FocusEvent e) {
658
				if (document.getLength() == 0) {
659
					initEmptyText();
660
				}
661
			}
662

    
663
		});
664
		textControl.addFocusListener(getFocusListener());
665

    
666
		if (document.getLength() == 0) {
667
			textControl.setFont(AbstractUtility
668
					.getFont(Resources.FONT_DEFAULT_PROMPT));
669
			document.set(prompt);
670
		}
671
	}
672

    
673
	abstract protected Font getViewerFont();
674

    
675
	protected void initEmptyText() {
676
		Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
677
		getNameViewer().getTextWidget().setFont(defaultFont);
678

    
679
		getNameViewer().getDocument().set(getEmptyTextPrompt());
680
	}
681

    
682
	protected void setFocusListener(FocusListener focusListener) {
683
		this.focusListener = focusListener;
684
	}
685

    
686
	private FocusListener getFocusListener() {
687
		return focusListener;
688
	}
689

    
690
	public void setDirty(boolean isDirty) {
691
		if (isDirty) {
692
			getEditor().getManagedForm().dirtyStateChanged();
693
		}
694
		this.isDirty = isDirty;
695
	}
696

    
697
	@Override
698
    public boolean isDirty() {
699
		return isDirty;
700
	}
701

    
702
	public void setMenu() {
703
        getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
704
	}
705

    
706
	private Control[] draggableControls;
707

    
708
	protected void setDraggableControl(Control[] controls) {
709
		draggableControls = controls;
710
	}
711

    
712
	public void setIsDraggable(boolean draggable) {
713

    
714
		if (draggable) {
715

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

    
721
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
722
			int operations = DND.DROP_MOVE;
723

    
724
			for (Control draggableControl : draggableControls) {
725
				DragSource dragSource = new DragSource(draggableControl,
726
						operations);
727
				dragSource.setTransfer(types);
728

    
729
				dragSource.addDragListener(new NameEditorDragListenerE4(this));
730
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
731
						control));
732
			}
733
		}
734
	}
735

    
736
	private String nonEditableText;
737

    
738
	ControlListener nonEditableResizeListener = new ControlAdapter() {
739

    
740
		int width = 0;
741

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

    
755
	private String nonEditableHoverText;
756

    
757
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
758

    
759
	private T data;
760

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

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

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

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

    
807
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
808
			nonEditableInfoHover.setRespectDisplayBounds(true);
809

    
810
		}
811
		nonEditableInfoHover.setText(nonEditableHoverText);
812
		nonEditableInfoLabel.setText(nonEditableText);
813

    
814
		calculateAnnotations();
815
	}
816

    
817
	@Override
818
    public T getData() {
819
		return data;
820
	}
821

    
822
	public void setData(T data) {
823
		this.data = HibernateProxyHelper.deproxy(data);
824
	}
825

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

    
838
		};
839

    
840
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
841

    
842
	}
843

    
844
	abstract class LabelEllipsisListener extends ControlAdapter {
845

    
846
		private final Label label;
847
		int width = 0;
848

    
849
		LabelEllipsisListener(Label label) {
850
			this.label = label;
851
		}
852

    
853
		abstract public String getLabelText();
854

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

    
867
	public void storeCursor() {
868
		this.cursorPosition = getNameViewer().getCursorPosition();
869
	}
870

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

    
883
	public void restoreColor() {
884
		setBackground(backgroundColor);
885
	}
886

    
887
	@Override
888
	public void initialize(IManagedForm form) {
889
		// TODO Auto-generated method stub
890
	}
891

    
892
	@Override
893
	public void dispose() {
894
		if (getControl() != null) {
895
			getControl().dispose();
896
		}
897
	}
898

    
899
	@Override
900
	public void commit(boolean onSave) {
901
		if (isDirty()) {
902
			persistName();
903
		}
904
	}
905

    
906
	@Override
907
	public boolean setFormInput(Object input) {
908
		return false;
909
	}
910

    
911
	@Override
912
	public void setFocus() {
913
		getNameViewer().getControl().setFocus();
914
	}
915

    
916
	@Override
917
	public boolean isStale() {
918
		return false;
919
	}
920

    
921
	public void setDisabled(boolean disabled) {
922
		setEnabled(!disabled);
923
	}
924

    
925
	public void setEnabled(boolean enabled) {
926
		Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
927

    
928
		getNameViewer().getTextWidget().setEditable(enabled);
929
		getNameViewer().getTextWidget().setForeground(color);
930
	}
931

    
932

    
933
}
(2-2/11)