Project

General

Profile

Download (24 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2007 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9

    
10
package eu.etaxonomy.taxeditor.editor.name.e4.container;
11

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

    
16
import org.apache.commons.lang3.StringUtils;
17
import org.eclipse.core.runtime.Assert;
18
import org.eclipse.e4.ui.di.UISynchronize;
19
import org.eclipse.jface.dialogs.Dialog;
20
import org.eclipse.jface.text.IDocument;
21
import org.eclipse.jface.text.Position;
22
import org.eclipse.jface.window.DefaultToolTip;
23
import org.eclipse.swt.custom.StyledText;
24
import org.eclipse.swt.dnd.DND;
25
import org.eclipse.swt.dnd.DragSource;
26
import org.eclipse.swt.dnd.Transfer;
27
import org.eclipse.swt.events.ControlAdapter;
28
import org.eclipse.swt.events.ControlEvent;
29
import org.eclipse.swt.events.ControlListener;
30
import org.eclipse.swt.events.FocusAdapter;
31
import org.eclipse.swt.events.FocusEvent;
32
import org.eclipse.swt.events.FocusListener;
33
import org.eclipse.swt.events.ModifyEvent;
34
import org.eclipse.swt.events.ModifyListener;
35
import org.eclipse.swt.events.MouseAdapter;
36
import org.eclipse.swt.events.MouseEvent;
37
import org.eclipse.swt.graphics.Color;
38
import org.eclipse.swt.graphics.Font;
39
import org.eclipse.swt.graphics.Image;
40
import org.eclipse.swt.widgets.Composite;
41
import org.eclipse.swt.widgets.Control;
42
import org.eclipse.swt.widgets.Display;
43
import org.eclipse.swt.widgets.Label;
44
import org.eclipse.ui.forms.IFormPart;
45
import org.eclipse.ui.forms.IManagedForm;
46
import org.eclipse.ui.forms.widgets.TableWrapData;
47
import org.eclipse.ui.forms.widgets.TableWrapLayout;
48

    
49
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
50
import eu.etaxonomy.cdm.model.common.CdmBase;
51
import eu.etaxonomy.cdm.model.name.TaxonName;
52
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
53
import eu.etaxonomy.cdm.strategy.parser.ParserProblem;
54
import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
55
import eu.etaxonomy.taxeditor.editor.l10n.Messages;
56
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation;
57
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation.EditorAnnotationType;
58
import eu.etaxonomy.taxeditor.editor.name.container.IContainerConstants;
59
import eu.etaxonomy.taxeditor.editor.name.container.LineBreakListener;
60
import eu.etaxonomy.taxeditor.editor.name.container.LineWrapSupport;
61
import eu.etaxonomy.taxeditor.editor.name.e4.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
	public AbstractGroupedContainerE4(AbstractGroupE4 group, T taxonBase) {
123
		setData(taxonBase);
124
		this.group = group;
125
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
126
	}
127

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

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

    
135
		setMenu();
136

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

    
140
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
141

    
142
		initializeComposite();
143

    
144
		createListener();
145

    
146
		enableFreeText();
147
	}
148

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

    
156
				TaxonName name = (TaxonName)parseHandler.parse(text);
157
				getTaxonBase().setName(name);
158
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
159

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

    
168
		nameCompositeFocusListener = new FocusAdapter() {
169

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

    
177
		addListener();
178
	}
179

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

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

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

    
199
	protected String getEmptyTextPrompt() {
200
		return EMPTY_NAME_PROMPT;
201
	}
202

    
203
	protected void initTextViewer() {
204

    
205
		updateIndent();
206

    
207
		updateIcon();
208

    
209
		String text = NameHelper.getDisplayNameWithRef(getData());
210

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

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

    
225
	public void showAnnotations() {
226

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

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

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

    
245
	}
246

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

    
253
		List<ParserProblem> parsingProblems = name.getParsingProblems();
254

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

    
261
	private Position getParsingProblemPosition() {
262
		String text = getNameViewer().getTextWidget().getText();
263

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

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

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

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

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

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

    
303
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
304

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

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

    
318
		updateNonEditableInfo();
319

    
320
		updateIcon();
321
		// placeCursor();
322
		updateIndent();
323
		setDelayedSelection();
324
		enableFreeText();
325
	}
326

    
327
	protected abstract void updateIcon();
328

    
329
	protected abstract void updateIndent();
330

    
331
	protected abstract void updateNonEditableInfo();
332

    
333
	protected void enableFreeText() {
334
		setEnabled(isFreetextEditingAllowed());
335
	}
336

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

    
346
		enableFreetext |= isNameUsedMultipleTimes();
347
		enableFreetext &= isNameParsable();
348

    
349
		return enableFreetext;
350
	}
351

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

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

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

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

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

    
394
		return isParsable;
395
	}
396

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

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

    
407
        return result;
408
    }
409

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

    
421
	public T getTaxonBase() {
422
		return getData();
423
	}
424

    
425
	public TaxonName getName() {
426
		return CdmBase.deproxy(getTaxonBase().getName());
427
	}
428

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

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

    
450
	public void remove() {
451
		getGroup().remove(this);
452
	}
453

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

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

    
465
		layout.verticalSpacing = 0;
466
		layout.horizontalSpacing = 0;
467

    
468
		control.setLayout(layout);
469

    
470
	}
471

    
472
	protected TaxonNameEditorE4 getEditor() {
473
		return getGroup().getEditor();
474
	}
475

    
476
	public Composite getControl() {
477
		return control;
478
	}
479

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

    
484
	protected void createTextViewer() {
485
		nameViewer = new NameViewerE4(control);
486

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

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

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

    
518
	public void setIcon(Image icon) {
519
		getNameViewer().setIcon(icon);
520
	}
521

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

    
535
	public void setSelected() {
536
		getNameViewer().getTextWidget().setFocus();
537
	}
538

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

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

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

    
558
			backgroundColor = AbstractUtility.getColor(colorString);
559

    
560
			setBackground(backgroundColor);
561
		}
562
	}
563

    
564
	private Delay delay = new Delay(Display.getCurrent());
565

    
566
	private class Delay extends Thread{
567

    
568
	    private volatile boolean stop = false;
569
        private Display display;
570

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

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

    
597
	protected void setDelayedSelection() {
598
		selection = this;
599

    
600
		Display display = Display.getCurrent();
601

    
602
		delay.requestStop();
603
		delay = new Delay(display);
604
		delay.start();
605

    
606
	}
607

    
608
	public void setBackground(Color color) {
609
		control.setBackground(color);
610

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

    
615
		getNameViewer().setBackground(color);
616
	}
617

    
618
	public void setFont(Font font) {
619
		getNameViewer().getTextWidget().setFont(font);
620
	}
621

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

    
631
	public void createEmptyViewerPrompt(final String prompt) {
632

    
633
		Assert.isNotNull(getNameViewer());
634

    
635
		final StyledText textControl = getNameViewer().getTextWidget();
636
		final IDocument document = getNameViewer().getDocument();
637

    
638
		setFocusListener(new FocusListener() {
639

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

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

    
655
		});
656
		textControl.addFocusListener(getFocusListener());
657

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

    
665
	abstract protected Font getViewerFont();
666

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

    
671
		getNameViewer().getDocument().set(getEmptyTextPrompt());
672
	}
673

    
674
	protected void setFocusListener(FocusListener focusListener) {
675
		this.focusListener = focusListener;
676
	}
677

    
678
	private FocusListener getFocusListener() {
679
		return focusListener;
680
	}
681

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

    
689
	@Override
690
    public boolean isDirty() {
691
		return isDirty;
692
	}
693

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

    
698
	private Control[] draggableControls;
699

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

    
704
	public void setIsDraggable(boolean draggable) {
705

    
706
		if (draggable) {
707

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

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

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

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

    
728
	private String nonEditableText;
729

    
730
	ControlListener nonEditableResizeListener = new ControlAdapter() {
731

    
732
		int width = 0;
733

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

    
747
	private String nonEditableHoverText;
748

    
749
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
750

    
751
	private T data;
752

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

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

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

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

    
799
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
800
			nonEditableInfoHover.setRespectDisplayBounds(true);
801

    
802
		}
803
		nonEditableInfoHover.setText(nonEditableHoverText);
804
		nonEditableInfoLabel.setText(nonEditableText);
805

    
806
		calculateAnnotations();
807
	}
808

    
809
	@Override
810
    public T getData() {
811
		return data;
812
	}
813

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

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

    
830
		};
831

    
832
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
833

    
834
	}
835

    
836
	abstract class LabelEllipsisListener extends ControlAdapter {
837

    
838
		private final Label label;
839
		int width = 0;
840

    
841
		LabelEllipsisListener(Label label) {
842
			this.label = label;
843
		}
844

    
845
		abstract public String getLabelText();
846

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

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

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

    
875
	public void restoreColor() {
876
		setBackground(backgroundColor);
877
	}
878

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

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

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

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

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

    
908
	@Override
909
	public boolean isStale() {
910
		return false;
911
	}
912

    
913
	public void setDisabled(boolean disabled) {
914
		setEnabled(!disabled);
915
	}
916

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

    
920
		getNameViewer().getTextWidget().setEditable(enabled);
921
		getNameViewer().getTextWidget().setForeground(color);
922
	}
923

    
924

    
925
}
(2-2/11)