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 = (ModifyEvent e)->{
155
				// mark the composite dirty
156
				setDirty(true);
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
				// show errors resulting from parsing
165
				calculateAnnotations();
166
				// store the position of the cursor
167
				storeCursor();
168
				// notify selection listener
169
				setDelayedSelection();
170
		};
171
		
172
		nameCompositeFocusListener = new FocusAdapter() {
173

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

    
178
				persistName();
179
			}
180
		};
181

    
182
		addListener();
183
	}
184

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

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

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

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

    
208
	protected void initTextViewer() {
209

    
210
		updateIndent();
211

    
212
		updateIcon();
213

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

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

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

    
230
	public void showAnnotations() {
231

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

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

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

    
250
	}
251

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

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

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

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

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

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

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

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

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

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

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

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

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

    
323
		updateNonEditableInfo();
324

    
325
		updateIcon();
326
		// placeCursor();
327
		updateIndent();
328

    
329
		enableFreeText();
330
	}
331

    
332
	protected abstract void updateIcon();
333

    
334
	protected abstract void updateIndent();
335

    
336
	protected abstract void updateNonEditableInfo();
337

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

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

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

    
354
		return enableFreetext;
355
	}
356

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

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

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

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

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

    
399
		return isParsable;
400
	}
401

    
402
	/**
403
     * @param name
404
     * @return
405
     */
406
    private boolean nameAuthorsSet(TaxonName name) {
407
       boolean result = true;
408
       result &= name.getBasionymAuthorship() == null && name.getCombinationAuthorship() == null && name.getExBasionymAuthorship() == null && name.getExCombinationAuthorship() == null;
409
       return result;
410
    }
411

    
412
    /**
413
     * @param name
414
     * @return
415
     */
416
    private boolean nameEpithetsNotSet(TaxonName name) {
417
        boolean result = true;
418
        result &= name.getGenusOrUninomial() == null && name.getInfraGenericEpithet() == null && name.getSpecificEpithet() == null && name.getInfraSpecificEpithet() == null;
419

    
420
        return result;
421
    }
422

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

    
434
	public T getTaxonBase() {
435
		return getData();
436
	}
437

    
438
	public TaxonName getName() {
439
		return CdmBase.deproxy(getTaxonBase().getName());
440
	}
441

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

    
456
	public AbstractGroupE4 getGroup() {
457
		if (group == null) {
458
			throw new IllegalStateException("Group shall not be null."); //$NON-NLS-1$
459
		}
460
		return group;
461
	}
462

    
463
	public void remove() {
464
		getGroup().remove(this);
465
	}
466

    
467
	protected void createControl() {
468
		control = getEditor().getToolkit().createComposite(
469
				getGroup().getControl());
470

    
471
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
472
		TableWrapLayout layout = new TableWrapLayout();
473
		layout.leftMargin = 0;
474
		layout.rightMargin = 0;
475
		layout.topMargin = 5;
476
		layout.bottomMargin = 5;
477

    
478
		layout.verticalSpacing = 0;
479
		layout.horizontalSpacing = 0;
480

    
481
		control.setLayout(layout);
482

    
483
	}
484

    
485
	protected TaxonNameEditorE4 getEditor() {
486
		return getGroup().getEditor();
487
	}
488

    
489
	public Composite getControl() {
490
		return control;
491
	}
492

    
493
	protected void createLineWrapSupport() {
494
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
495
	}
496

    
497
	protected void createTextViewer() {
498
		nameViewer = new NameViewer(control);
499

    
500
		focusListener = new FocusAdapter() {
501
			@Override
502
			public void focusGained(FocusEvent e) {
503
			    if (getEditor()
504
                        .getGroupedContainers().isEmpty()){
505
			        return;
506
			    }
507

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

    
520
		MouseAdapter mouseListener = new MouseAdapter() {
521
			@Override
522
			public void mouseDown(MouseEvent e) {
523
				storeCursor();
524
			}
525
		};
526
		control.addMouseListener(mouseListener);
527
		nameViewer.getRulerControl().addMouseListener(mouseListener);
528
		nameViewer.getTextWidget().addMouseListener(mouseListener);
529
	}
530

    
531
	public void setIcon(Image icon) {
532
		getNameViewer().setIcon(icon);
533
	}
534

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

    
548
	public void setSelected() {
549
		getNameViewer().getTextWidget().setFocus();
550
	}
551

    
552
	public boolean isSelected() {
553
		return getEditor().getSelectedContainer() == this;
554
	}
555

    
556
	public void colorSelected(int mode) {
557
		if (!control.isDisposed()) {
558
			String colorString = null;
559

    
560
			switch (mode) {
561
			case SELECTED_FOCUS:
562
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
563
				break;
564
			case SELECTED_NO_FOCUS:
565
				colorString = Resources.COLOR_CONTROL_SELECTED;
566
				break;
567
			default:
568
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
569
			}
570

    
571
			backgroundColor = AbstractUtility.getColor(colorString);
572

    
573
			setBackground(backgroundColor);
574
		}
575
	}
576

    
577
	private Delay delay = new Delay(Display.getCurrent());
578

    
579
	private class Delay extends Thread{
580

    
581
	    private volatile boolean stop = false;
582
        private Display display;
583

    
584
        public Delay(Display display) {
585
            this.display = display;
586
        }
587
        @Override
588
        public void run() {
589

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

    
610
	protected void setDelayedSelection() {
611
		selection = this;
612

    
613
		Display display = Display.getCurrent();
614

    
615
		delay.requestStop();
616
		delay = new Delay(display);
617
		delay.start();
618

    
619
	}
620

    
621
	public void setBackground(Color color) {
622
		control.setBackground(color);
623

    
624
		for (Control child : control.getChildren()) {
625
			child.setBackground(color);
626
		}
627

    
628
		getNameViewer().setBackground(color);
629
	}
630

    
631
	public void setFont(Font font) {
632
		getNameViewer().getTextWidget().setFont(font);
633
	}
634

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

    
644
	public void createEmptyViewerPrompt(final String prompt) {
645

    
646
		Assert.isNotNull(getNameViewer());
647

    
648
		final StyledText textControl = getNameViewer().getTextWidget();
649
		final IDocument document = getNameViewer().getDocument();
650

    
651
		setFocusListener(new FocusListener() {
652

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

    
661
			@Override
662
            public void focusLost(FocusEvent e) {
663
				if (document.getLength() == 0) {
664
					initEmptyText();
665
				}
666
			}
667

    
668
		});
669
		textControl.addFocusListener(getFocusListener());
670

    
671
		if (document.getLength() == 0) {
672
			textControl.setFont(AbstractUtility
673
					.getFont(Resources.FONT_DEFAULT_PROMPT));
674
			document.set(prompt);
675
		}
676
	}
677

    
678
	abstract protected Font getViewerFont();
679

    
680
	protected void initEmptyText() {
681
		Font defaultFont = AbstractUtility.getFont(Resources.FONT_DEFAULT_PROMPT);
682
		getNameViewer().getTextWidget().setFont(defaultFont);
683

    
684
		getNameViewer().getDocument().set(getEmptyTextPrompt());
685
		placeCursor();
686
	}
687

    
688
	protected void setFocusListener(FocusListener focusListener) {
689
		this.focusListener = focusListener;
690
	}
691

    
692
	private FocusListener getFocusListener() {
693
		return focusListener;
694
	}
695

    
696
	public void setDirty(boolean isDirty) {
697
		if (isDirty) {
698
			getEditor().getManagedForm().dirtyStateChanged();
699
		}
700
		this.isDirty = isDirty;
701
	}
702

    
703
	@Override
704
    public boolean isDirty() {
705
		return isDirty;
706
	}
707

    
708
	public void setMenu() {
709
        getEditor().getMenuService().registerContextMenu(getNameViewer().getTextWidget(), "eu.etaxonomy.taxeditor.editor.popupmenu.nameeditor");
710
	}
711

    
712
	private Control[] draggableControls;
713

    
714
	protected void setDraggableControl(Control[] controls) {
715
		draggableControls = controls;
716
	}
717

    
718
	public void setIsDraggable(boolean draggable) {
719

    
720
		if (draggable) {
721

    
722
			if (draggableControls == null) {
723
				throw new NullPointerException(
724
						"Draggable controls must be set to add draggability"); //$NON-NLS-1$
725
			}
726

    
727
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
728
			int operations = DND.DROP_MOVE;
729

    
730
			for (Control draggableControl : draggableControls) {
731
				DragSource dragSource = new DragSource(draggableControl,
732
						operations);
733
				dragSource.setTransfer(types);
734

    
735
				dragSource.addDragListener(new NameEditorDragListenerE4(this));
736
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(
737
						control));
738
			}
739
		}
740
	}
741

    
742
	private String nonEditableText;
743

    
744
	ControlListener nonEditableResizeListener = new ControlAdapter() {
745

    
746
		int width = 0;
747

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

    
761
	private String nonEditableHoverText;
762

    
763
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
764

    
765
	private T data;
766

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

    
783
		if (append) {
784
			nonEditableText += ", " + info; //$NON-NLS-1$
785
			nonEditableHoverText += "\n" + info; //$NON-NLS-1$
786
		} else {
787
			nonEditableText = info;
788
			nonEditableHoverText = info;
789
		}
790

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

    
803
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(
804
					nonEditableInfoLabel) {
805
				@Override
806
				public String getLabelText() {
807
					return nonEditableText.toUpperCase();
808
				}
809
			};
810
			nonEditableInfoLabel
811
					.addControlListener(nonEditableLabelEllipsisListener);
812

    
813
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
814
			nonEditableInfoHover.setRespectDisplayBounds(true);
815

    
816
		}
817
		nonEditableInfoHover.setText(nonEditableHoverText);
818
		nonEditableInfoLabel.setText(nonEditableText);
819

    
820
		calculateAnnotations();
821
	}
822

    
823
	@Override
824
    public T getData() {
825
		return data;
826
	}
827

    
828
	public void setData(T data) {
829
		this.data = HibernateProxyHelper.deproxy(data);
830
	}
831

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

    
844
		};
845

    
846
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
847

    
848
	}
849

    
850
	abstract class LabelEllipsisListener extends ControlAdapter {
851

    
852
		private final Label label;
853
		int width = 0;
854

    
855
		LabelEllipsisListener(Label label) {
856
			this.label = label;
857
		}
858

    
859
		abstract public String getLabelText();
860

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

    
873
	public void storeCursor() {
874
		this.cursorPosition = getNameViewer().getCursorPosition();
875
	}
876

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

    
889
	public void restoreColor() {
890
		setBackground(backgroundColor);
891
	}
892

    
893
	@Override
894
	public void initialize(IManagedForm form) {
895
		// TODO Auto-generated method stub
896

    
897
	}
898

    
899
	@Override
900
	public void dispose() {
901
		if (getControl() != null) {
902
			setMenu();
903
			getControl().dispose();
904
		}
905
	}
906

    
907
	@Override
908
	public void commit(boolean onSave) {
909
		if (isDirty()) {
910
			persistName();
911
		}
912
	}
913

    
914
	@Override
915
	public boolean setFormInput(Object input) {
916
		return false;
917
	}
918

    
919
	@Override
920
	public void setFocus() {
921
		getNameViewer().getControl().setFocus();
922
	}
923

    
924
	@Override
925
	public boolean isStale() {
926
		return false;
927
	}
928

    
929
	public void setDisabled(boolean disabled) {
930
		setEnabled(!disabled);
931
	}
932

    
933
	public void setEnabled(boolean enabled) {
934
		Color color = enabled ? control.getForeground() : AbstractUtility.getColor(Resources.COLOR_DISABLED_EDITOR);
935

    
936
		getNameViewer().getTextWidget().setEditable(enabled);
937
		getNameViewer().getTextWidget().setForeground(color);
938
	}
939

    
940

    
941
}
(2-2/11)