Project

General

Profile

Download (23 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.jface.dialogs.Dialog;
19
import org.eclipse.jface.text.IDocument;
20
import org.eclipse.jface.text.Position;
21
import org.eclipse.jface.viewers.ISelectionChangedListener;
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.container.NameViewer;
62
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragSourceEffect;
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.operation.CreateSynonymInNewGroupOperation;
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 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 AbstractGroupedContainerE4<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 AbstractGroupE4 group;
107

    
108
	private Label nonEditableInfoLabel;
109
	private DefaultToolTip nonEditableInfoHover;
110

    
111
	private static AbstractGroupedContainerE4 selection;
112

    
113
	private FocusListener focusListener;
114
	private LineBreakListener lineBreakListener;
115

    
116
	private int cursorPosition;
117

    
118
	protected Composite control;
119

    
120
	private Color backgroundColor;
121
	private boolean isDirty;
122

    
123
    private ISelectionChangedListener selectionChangedListener;
124

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

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

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

    
137
		setMenu();
138

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

    
142
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
143

    
144
		initializeComposite();
145

    
146
		createListener();
147

    
148
		enableFreeText();
149
	}
150

    
151
	protected void createListener() {
152
		nameCompositeModifyListener = new ModifyListener() {
153

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

    
161
				TaxonName name = (TaxonName)parseHandler.parse(text);
162
				getTaxonBase().setName(name);
163
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
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
			}
172
		};
173
		nameCompositeFocusListener = new FocusAdapter() {
174

    
175
			@Override
176
			public void focusLost(FocusEvent e) {
177
				super.focusLost(e);
178

    
179
				persistName();
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()));
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

    
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

    
394
		isParsable &= !name.isProtectedAuthorshipCache();
395
		isParsable &= !name.isProtectedNameCache();
396

    
397
		return isParsable;
398
	}
399

    
400
	/**
401
	 * Parse the text and calculate errors
402
	 */
403
	public void parseAndCalculateAnnotations() {
404
		removeListener();
405
		String unparsedNameString = getNameViewer().getTextWidget().getText();
406
		parseHandler.parse(unparsedNameString);
407
		addListener();
408
		calculateAnnotations();
409
	}
410

    
411
	public T getTaxonBase() {
412
		return getData();
413
	}
414

    
415
	public TaxonName getName() {
416
		return CdmBase.deproxy(getTaxonBase().getName());
417
	}
418

    
419
	public void persistName() {
420
		if (isDirty()) {
421
			getNameViewer().getTextWidget().setEnabled(false);
422
			final String unparsedNameString = getNameViewer().getTextWidget()
423
					.getText();
424
			final TaxonName name = (TaxonName)parseHandler
425
					.parseAndResolveDuplicates(unparsedNameString);
426
			getTaxonBase().setName(name);
427
			getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
428
			setDirty(false);
429
			getNameViewer().getTextWidget().setEnabled(true);
430
		}
431
	}
432

    
433
	public AbstractGroupE4 getGroup() {
434
		if (group == null) {
435
			throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
436
		}
437
		return group;
438
	}
439

    
440
	public void remove() {
441
		getGroup().remove(this);
442
	}
443

    
444
	protected void createControl() {
445
		control = getEditor().getToolkit().createComposite(
446
				getGroup().getControl());
447

    
448
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
449
		TableWrapLayout layout = new TableWrapLayout();
450
		layout.leftMargin = 0;
451
		layout.rightMargin = 0;
452
		layout.topMargin = 5;
453
		layout.bottomMargin = 5;
454

    
455
		layout.verticalSpacing = 0;
456
		layout.horizontalSpacing = 0;
457

    
458
		control.setLayout(layout);
459

    
460
	}
461

    
462
	protected TaxonNameEditorE4 getEditor() {
463
		return getGroup().getEditor();
464
	}
465

    
466
	public Composite getControl() {
467
		return control;
468
	}
469

    
470
	protected void createLineWrapSupport() {
471
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
472
	}
473

    
474
	protected void createTextViewer() {
475
		nameViewer = new NameViewer(control);
476

    
477
		focusListener = new FocusAdapter() {
478
			@Override
479
			public void focusGained(FocusEvent e) {
480
				if(!enabled){
481
					return;
482
				}
483
				for (AbstractGroupedContainerE4 container : getEditor()
484
						.getGroupedContainers()) {
485
					container.colorSelected(NOT_SELECTED);
486
				}
487
				getEditor().getManagedForm().setInput(
488
						AbstractGroupedContainerE4.this);
489
				placeCursor();
490
				colorSelected(SELECTED_FOCUS);
491
			}
492
		};
493
		nameViewer.getTextWidget().addFocusListener(focusListener);
494

    
495
		MouseAdapter mouseListener = new MouseAdapter() {
496
			@Override
497
			public void mouseDown(MouseEvent e) {
498
				storeCursor();
499
			}
500
		};
501
		control.addMouseListener(mouseListener);
502
		nameViewer.getRulerControl().addMouseListener(mouseListener);
503
		nameViewer.getTextWidget().addMouseListener(mouseListener);
504
	}
505

    
506
	public void setIcon(Image icon) {
507
		getNameViewer().setIcon(icon);
508
	}
509

    
510
	public void setIndent(int indent) {
511
		if (control.getLayout() instanceof TableWrapLayout) {
512
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
513
			layout.leftMargin = indent;
514
			layout.rightMargin = ACCEPTED_INDENT;
515
			control.setLayout(layout);
516
			control.layout();
517
		} else {
518
			throw new RuntimeException(
519
					"Couldn't indent - composite's layout must be TableWrapLayout."); //$NON-NLS-1$
520
		}
521
	}
522

    
523
	public void setSelected() {
524
		getNameViewer().getTextWidget().setFocus();
525
	}
526

    
527
	public boolean isSelected() {
528
		return getEditor().getSelectedContainer() == this;
529
	}
530

    
531
	public void colorSelected(int mode) {
532
		if (!control.isDisposed()) {
533
			String colorString = null;
534

    
535
			switch (mode) {
536
			case SELECTED_FOCUS:
537
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
538
				break;
539
			case SELECTED_NO_FOCUS:
540
				colorString = Resources.COLOR_CONTROL_SELECTED;
541
				break;
542
			default:
543
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
544
			}
545

    
546
			backgroundColor = AbstractUtility.getColor(colorString);
547

    
548
			setBackground(backgroundColor);
549
		}
550
	}
551

    
552
	protected void setDelayedSelection() {
553
		// TODO this might be done better
554
		// this is the quickest solution i could come up with and it improves
555
		// performance
556
		// please reimplement if you know better.
557
		selection = this;
558

    
559
		// start timer
560
		Display display = Display.getCurrent();
561
		Runnable runnable = new Runnable() {
562

    
563
			@Override
564
            public void run() {
565
				getEditor().getManagedForm().setInput(selection);
566
			}
567
		};
568
		display.timerExec(1000, runnable);
569

    
570
	}
571

    
572
	public void setBackground(Color color) {
573
		control.setBackground(color);
574

    
575
		for (Control child : control.getChildren()) {
576
			child.setBackground(color);
577
		}
578

    
579
		getNameViewer().setBackground(color);
580
	}
581

    
582
	public void setFont(Font font) {
583
		getNameViewer().getTextWidget().setFont(font);
584
	}
585

    
586
	public NameViewer getNameViewer() {
587
		if (nameViewer == null) {
588
			throw new RuntimeException(
589
					"The Name Viewer is corrupt for Name Container: " //$NON-NLS-1$
590
							+ getTaxonBase().getName().getTitleCache());
591
		}
592
		return nameViewer;
593
	}
594

    
595
	public void createEmptyViewerPrompt(final String prompt) {
596

    
597
		Assert.isNotNull(getNameViewer());
598

    
599
		final StyledText textControl = getNameViewer().getTextWidget();
600
		final IDocument document = getNameViewer().getDocument();
601

    
602
		setFocusListener(new FocusListener() {
603

    
604
			@Override
605
            public void focusGained(FocusEvent e) {
606
				if (document.get().equals(prompt)) {
607
					textControl.setFont(getViewerFont());
608
					document.set(""); //$NON-NLS-1$
609
				}
610
			}
611

    
612
			@Override
613
            public void focusLost(FocusEvent e) {
614
				if (document.getLength() == 0) {
615
					initEmptyText();
616
				}
617
			}
618

    
619
		});
620
		textControl.addFocusListener(getFocusListener());
621

    
622
		if (document.getLength() == 0) {
623
			textControl.setFont(AbstractUtility
624
					.getFont(Resources.FONT_DEFAULT_PROMPT));
625
			document.set(prompt);
626
		}
627
	}
628

    
629
	abstract protected Font getViewerFont();
630

    
631
	protected void initEmptyText() {
632
		Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
633
		getNameViewer().getTextWidget().setFont(defaultFont);
634

    
635
		getNameViewer().getDocument().set(getEmptyTextPrompt());
636
		placeCursor();
637
	}
638

    
639
	protected void setFocusListener(FocusListener focusListener) {
640
		this.focusListener = focusListener;
641
	}
642

    
643
	private FocusListener getFocusListener() {
644
		return focusListener;
645
	}
646

    
647
	public void setDirty(boolean isDirty) {
648
		if (isDirty) {
649
			getEditor().getManagedForm().dirtyStateChanged();
650
		}
651
		this.isDirty = isDirty;
652
	}
653

    
654
	@Override
655
    public boolean isDirty() {
656
		return isDirty;
657
	}
658

    
659
	public void setMenu() {
660
        getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
661
	}
662

    
663
	private Control[] draggableControls;
664

    
665
	protected void setDraggableControl(Control[] controls) {
666
		draggableControls = controls;
667
	}
668

    
669
	public void setIsDraggable(boolean draggable) {
670

    
671
		if (draggable) {
672

    
673
			if (draggableControls == null) {
674
				throw new NullPointerException(
675
						"Draggable controls must be set to add draggability"); //$NON-NLS-1$
676
			}
677

    
678
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
679
			int operations = DND.DROP_MOVE;
680

    
681
			for (Control draggableControl : draggableControls) {
682
				DragSource dragSource = new DragSource(draggableControl,
683
						operations);
684
				dragSource.setTransfer(types);
685

    
686
				dragSource.addDragListener(new NameEditorDragListenerE4(this));
687
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
688
						control));
689
			}
690
		}
691
	}
692

    
693
	private String nonEditableText;
694

    
695
	ControlListener nonEditableResizeListener = new ControlAdapter() {
696

    
697
		int width = 0;
698

    
699
		@Override
700
		public void controlResized(ControlEvent e) {
701
			if (nonEditableInfoLabel.getBounds().width == width) {
702
				return;
703
			}
704
			width = nonEditableInfoLabel.getBounds().width;
705
			if (nonEditableInfoLabel.getBounds().width > 0) {
706
				nonEditableInfoLabel.setText(Dialog.shortenText(
707
						nonEditableText.toUpperCase(), nonEditableInfoLabel));
708
			}
709
		}
710
	};
711

    
712
	private String nonEditableHoverText;
713

    
714
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
715

    
716
	private T data;
717

    
718
	private boolean enabled;
719

    
720
	/**
721
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
722
	 * input field. For instance, NameComposites display things like name
723
	 * relations, sec. references, etc. here.
724
	 *
725
	 * @param info
726
	 *            the text to display in the label
727
	 * @param append
728
	 *            whether the string should be appended to text that is already
729
	 *            shown in the label
730
	 */
731
	public void setNonEditableInfo(String info, boolean append) {
732
		// TODO non editable info should only be drawn once, when everything
733
		// else is drawn
734
		info = info.toUpperCase();
735

    
736
		if (append) {
737
			nonEditableText += ", " + info; //$NON-NLS-1$
738
			nonEditableHoverText += "\n" + info; //$NON-NLS-1$
739
		} else {
740
			nonEditableText = info;
741
			nonEditableHoverText = info;
742
		}
743

    
744
		if (nonEditableInfoLabel == null) {
745
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(
746
					control, ""); //$NON-NLS-1$
747
			TableWrapData layoutData = new TableWrapData(
748
					TableWrapData.FILL_GRAB, TableWrapData.TOP);
749
			// Set indent to viewer ruler's width
750
			if (getNameViewer().getRulerControl() != null) {
751
				// TODO right justify
752
				layoutData.indent = NameViewer.RULER_WIDTH;
753
			}
754
			nonEditableInfoLabel.setLayoutData(layoutData);
755

    
756
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
757
					nonEditableInfoLabel) {
758
				@Override
759
				public String getLabelText() {
760
					return nonEditableText.toUpperCase();
761
				}
762
			};
763
			nonEditableInfoLabel
764
					.addControlListener(nonEditableLabelEllipsisListener);
765

    
766
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
767
			nonEditableInfoHover.setRespectDisplayBounds(true);
768

    
769
		}
770
		nonEditableInfoHover.setText(nonEditableHoverText);
771
		nonEditableInfoLabel.setText(nonEditableText);
772

    
773
		calculateAnnotations();
774
	}
775

    
776
	@Override
777
    public T getData() {
778
		return data;
779
	}
780

    
781
	public void setData(T data) {
782
		this.data = HibernateProxyHelper.deproxy(data);
783
	}
784

    
785
	/**
786
	 * If the user hitting carriage return should cause something to happen -
787
	 * i.e. the creation of a new composite - call this method and override the
788
	 * method handleSplitText().
789
	 */
790
	protected void createLineBreakListener() {
791
		lineBreakListener = new LineBreakListener() {
792
			@Override
793
			public void handleSplitText(String text) {
794
				AbstractGroupedContainerE4.this.handleSplitText(text);
795
			}
796

    
797
		};
798

    
799
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
800

    
801
	}
802

    
803
	abstract class LabelEllipsisListener extends ControlAdapter {
804

    
805
		private final Label label;
806
		int width = 0;
807

    
808
		LabelEllipsisListener(Label label) {
809
			this.label = label;
810
		}
811

    
812
		abstract public String getLabelText();
813

    
814
		@Override
815
		public void controlResized(ControlEvent e) {
816
			if (label.getBounds().width == width) {
817
				return;
818
			}
819
			width = label.getBounds().width;
820
			if (label.getBounds().width > 0) {
821
				label.setText(TextHelper.shortenText(getLabelText(), label));
822
			}
823
		}
824
	}
825

    
826
	public void storeCursor() {
827
		this.cursorPosition = getNameViewer().getCursorPosition();
828
	}
829

    
830
	/**
831
	 * Puts the cursor to the position it was last seen on or to the end of line
832
	 * if no former position is known.
833
	 */
834
	public void placeCursor() {
835
		if (cursorPosition == 0) {
836
			getNameViewer().setCursorToEOL();
837
		} else {
838
			getNameViewer().setCursorPosition(cursorPosition);
839
		}
840
	}
841

    
842
	public void setGroup(AbstractGroupE4 group) {
843
		this.group = group;
844
	}
845

    
846
	public void restoreColor() {
847
		setBackground(backgroundColor);
848
	}
849

    
850
	@Override
851
	public void initialize(IManagedForm form) {
852
		// TODO Auto-generated method stub
853

    
854
	}
855

    
856
	@Override
857
	public void dispose() {
858
		if (getControl() != null) {
859
			setMenu();
860
			getControl().dispose();
861
		}
862
	}
863

    
864
	@Override
865
	public void commit(boolean onSave) {
866
		if (isDirty()) {
867
			persistName();
868
		}
869
	}
870

    
871
	@Override
872
	public boolean setFormInput(Object input) {
873
		return false;
874
	}
875

    
876
	@Override
877
	public void setFocus() {
878
		getNameViewer().getControl().setFocus();
879
	}
880

    
881
	@Override
882
	public boolean isStale() {
883
		return false;
884
	}
885

    
886
	public void setDisabled(boolean disabled) {
887
		this.enabled = !disabled;
888
		setEnabled(enabled);
889
	}
890

    
891
	public void setEnabled(boolean enabled) {
892
		this.enabled = enabled;
893
		Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
894

    
895
		getNameViewer().getTextWidget().setEditable(enabled);
896
		getNameViewer().getTextWidget().setEnabled(enabled);
897
		getNameViewer().getTextWidget().setForeground(color);
898
	}
899

    
900
	public boolean isEnabled(){
901
		return enabled;
902
	}
903
}
(2-2/11)