Project

General

Profile

Download (25.7 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.container;
11

    
12
import java.util.Set;
13

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

    
45
import eu.etaxonomy.cdm.common.CdmUtils;
46
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
47
import eu.etaxonomy.cdm.model.name.NameRelationship;
48
import eu.etaxonomy.cdm.model.name.NonViralName;
49
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
50
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
51
import eu.etaxonomy.taxeditor.editor.CdmDataTransfer;
52
import eu.etaxonomy.taxeditor.editor.EditorUtil;
53
import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
54
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragListener;
55
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragSourceEffect;
56
import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
57
import eu.etaxonomy.taxeditor.labels.ILabelImageStrategy;
58
import eu.etaxonomy.taxeditor.labels.LabelImageProvider;
59
import eu.etaxonomy.taxeditor.model.IElementHasDetails;
60
import eu.etaxonomy.taxeditor.model.NameHelper;
61
import eu.etaxonomy.taxeditor.model.TextHelper;
62
import eu.etaxonomy.taxeditor.parser.ParseHandler;
63
import eu.etaxonomy.taxeditor.preference.Resources;
64

    
65
/**
66
 * Formats <code>GroupedComposite</code> with cosmetic and layout properties specific to the
67
 * Editor. This should be used to maintain a consistent look and feel for all Editor
68
 * freetext area components, such as DescriptionElementComposite.
69
 * <p>
70
 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the contents
71
 * of {@link #getData()} when the <code>GroupedComposite</code> gets focus, i.e. to
72
 * populate the property sheet with the data.
73
 * </p>
74
 * <p>
75
 * The <code>IManagedForm</code> is also required to have a <code>Taxon</code> in its
76
 * own <code>getData()</code>.
77
 * </p>
78
 * <p>
79
 * The <code>IManagedForm</code> can also used for drawing borders by calling the method
80
 * <code>createBorderSupport()</code>.
81
 * </p>
82
 *
83
 * @author p.ciardelli
84
 * @author n.hoffmann
85
 * @created 02.06.2008
86
 * @version 1.0
87
 */
88
abstract public class AbstractGroupedContainer<T extends TaxonBase> implements IFormPart, IContainerConstants, IElementHasDetails {	
89

    
90
	protected ParseHandler parseHandler;
91
	
92
	private FocusListener nameCompositeFocusListener;
93
	private ModifyListener nameCompositeModifyListener;
94

    
95
	protected NameViewer nameViewer;
96
	
97
	private AbstractGroup group;
98
	
99
	private Label nonEditableInfoLabel;
100
	private DefaultToolTip nonEditableInfoHover;
101
	
102
	private static AbstractGroupedContainer selection;
103
	
104
	private FocusListener focusListener;
105
	private LineBreakListener lineBreakListener;
106

    
107
	private int cursorPosition;
108

    
109
	protected Composite control;
110

    
111
	private Color backgroundColor;
112
	private boolean isDirty;
113
	
114
	
115
	/**
116
	 * <p>Constructor for AbstractGroupedContainer.</p>
117
	 *
118
	 * @param editor a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor} object.
119
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
120
	 * @param taxonBase a T object.
121
	 * @param <T> a T object.
122
	 */
123
	public AbstractGroupedContainer(T taxonBase){
124
		setData(taxonBase);
125
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
126
	}
127
	
128
	public void createContent(){
129
		createControl();
130
		
131
		createTextViewer();
132
		createLineWrapSupport();
133
		createLineBreakListener();
134

    
135
		setMenu(getEditor().getMenu());
136
		
137
		setDraggableControl(new Control[]{getControl(), getNameViewer().getRulerControl()});
138
		
139
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
140
		
141
		
142
		initializeComposite();
143
		
144
		createListener();
145

    
146
		isFreeTextEnabled();
147
	}
148

    
149
	
150
	
151
	/**
152
	 * <p>createListener</p>
153
	 */
154
	protected void createListener(){
155
		nameCompositeModifyListener = new ModifyListener(){
156
			
157
			public void modifyText(ModifyEvent e) {
158
				// mark the composite dirty
159
				setDirty(true);
160
				// parse the text
161
				String text = nameViewer.getTextWidget().getText();
162
				
163
				NonViralName name = parseHandler.parse(text);
164
				getTaxonBase().setName(name);
165
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
166
				
167
				// show errors resulting from parsing
168
				calculateErrors();
169
				// store the position of the cursor				
170
				storeCursor();
171
				// notify selection listener
172
				setDelayedSelection();
173
			}
174
		};
175
		nameCompositeFocusListener = new FocusAdapter(){
176

    
177
			/* (non-Javadoc)
178
			 * @see org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt.events.FocusEvent)
179
			 */
180
			@Override
181
			public void focusLost(FocusEvent e) {
182
				super.focusLost(e);
183

    
184
				persistName();
185
			}		
186
		};
187
		
188
		addListener();
189
	}
190
	
191
	private void addListener(){
192
		getNameViewer().getTextWidget().addModifyListener(nameCompositeModifyListener);
193
		getNameViewer().getTextWidget().addFocusListener(nameCompositeFocusListener);
194
	}
195
	
196
	private void removeListener(){
197
		getNameViewer().getTextWidget().removeModifyListener(nameCompositeModifyListener);
198
		getNameViewer().getTextWidget().removeFocusListener(nameCompositeFocusListener);
199
	}
200
		
201
	/**
202
	 * Initialize the composite specific code
203
	 */
204
	protected abstract void initializeComposite();
205
	
206
	/**
207
	 * <p>getEmptyTextPrompt</p>
208
	 *
209
	 * @return a {@link java.lang.String} object.
210
	 */
211
	protected String getEmptyTextPrompt() {
212
		return EMPTY_NAME_PROMPT;
213
	}
214
	
215
	/**
216
	 * 
217
	 */
218
	private void showNameRelations() {
219
		TaxonNameBase<?, ?> name = getName();
220
		if (name == null) {
221
			return;
222
		}
223
		
224
		ILabelImageStrategy strategy = LabelImageProvider.getLabelStrategy(name);
225
		LabelImageProvider labelProvider = new LabelImageProvider(strategy);
226
		
227
		Set<NameRelationship> nameRelations = name.getNameRelations();
228
		if (nameRelations.size() == 0) {
229
			return;
230
		}
231
//		for (NameRelationship nameRelation : nameRelations) {
232
//			String typeLabel = null;
233
//			TaxonNameBase<?, ?> relatedName = null; 
234
//
235
//			if (name.equals(nameRelation.getFromName())) {
236
//				typeLabel = labelProvider.getNameRelationTypeLabel( 
237
//								nameRelation.getType());
238
//				relatedName = nameRelation.getToName();
239
//			} else {
240
//				typeLabel = labelProvider.getNameRelationTypeInverseLabel( 
241
//								nameRelation.getType());
242
//				relatedName = nameRelation.getFromName();
243
//			}
244
//			
245
//			setNonEditableInfo(typeLabel + " " + NameHelper.getDisplayName(relatedName));
246
//		}
247
	}
248
		
249
	/**
250
	 * <p>initTextViewer</p>
251
	 */
252
	protected void initTextViewer() {
253
		
254
//		showNameRelations();
255
		
256
		updateIndent();
257
		
258
		updateIcon();
259
		
260
		String text = NameHelper.getDisplayNameWithRef(getData());
261
		
262
		if (text.length() == 0) {
263
			initEmptyText();
264
		} else {
265
			getNameViewer().setText(text);
266
			placeCursor();
267
		}
268
		calculateErrors();
269
	}
270
	
271
	/**
272
	 * <p>calculateErrors</p>
273
	 */
274
	synchronized protected void calculateErrors() {
275
		getNameViewer().clearErrors();
276
		getNameViewer().setShowParsingErrors(getName());
277
	}
278

    
279
	/**
280
	 * <p>handleSplitText</p>
281
	 *
282
	 * @param text a {@link java.lang.String} object.
283
	 */
284
	protected void handleSplitText(String text) {
285
		// Create a synonym in a new homotypic group using text as name
286
		TaxonNameBase synonymName = ParseHandler.parseReferencedName(text, null);
287
		
288
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation
289
				("New Heterotypic Synonym", getEditor().getUndoContext(), getEditor().getTaxon(), synonymName, getEditor()));
290
	}
291

    
292
	/**
293
	 * Refreshes the display with latest data from the model.
294
	 *
295
	 * Note: Will not parse the text and not calculate errors!
296
	 */
297
	public void refresh() {
298
//		showNameRelations();
299
		
300
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
301
		
302
		if(getNameViewer().getTextWidget() == null){
303
			// we might get here via dnd. Look slike it can be ignored
304
			return;
305
		}
306
		
307
		if (text.length() == 0) {
308
			initEmptyText();
309
		} else if(! getNameViewer().getTextWidget().getText().equals(text)) {
310
			removeListener();
311
			getNameViewer().getTextWidget().setText(text);
312
			addListener();
313
		}
314
		
315
		updateNonEditableInfo();
316
		
317
		updateIcon();
318
//		placeCursor();
319
		updateIndent();
320
		
321
		isFreeTextEnabled();
322
	}
323

    
324
	/**
325
	 * 
326
	 */
327
	protected abstract void updateIcon();
328

    
329
	
330
	protected abstract void updateIndent();
331

    
332

    
333
	/**
334
	 * <p>updateNonEditableInfo</p>
335
	 */
336
	protected abstract void updateNonEditableInfo();
337
	
338
	/**
339
	 * 
340
	 */
341
	private void isFreeTextEnabled() {
342
		// Decide whether editing of freetext is allowed or not.
343
		if (freetextEditingAllowed()) {
344
			
345
			// set editable
346
			getNameViewer().getTextWidget().setEditable(true);
347
			
348
			// restore foreground font color again
349
			getNameViewer().getTextWidget().setForeground(control.getForeground());
350
		} else {
351
			// set non-editable
352
			getNameViewer().getTextWidget().setEditable(false);
353
			
354
			// grey out text as a non-editable indicator
355
			getNameViewer().getTextWidget().setForeground(EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR));
356
		}
357
	}
358
	
359

    
360
	/**
361
	 * Checks whether the freetext should be editable based on specific empty fields.
362
	 * @return
363
	 */
364
	private boolean freetextEditingAllowed() {
365
		NonViralName name = (NonViralName) HibernateProxyHelper.deproxy(getName());
366
		boolean enableFreetext = true;
367

    
368
		enableFreetext &= isNameUsedMultipleTimes(name);
369
		enableFreetext &= isNameParsable(name);
370
		
371
		if(!enableFreetext){
372
			getNameViewer().setShowNameNotParsableWarning(name);
373
		}
374
		
375
		return enableFreetext;
376
	}
377
	
378
	private boolean isNameUsedMultipleTimes(NonViralName name){
379
		if(name.getTaxonBases().size() != 1){
380
			getNameViewer().setShowMultipleNameUsageWarning(name);
381
			return false;
382
		}
383
		return true;
384
	}
385
	
386
	private boolean isNameParsable(NonViralName name){
387
		boolean isParsable = true; 
388
		isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); //taxonFieldsEmpty();
389
		
390
		isParsable &= ! name.isProtectedAuthorshipCache();
391
		isParsable &= ! name.isProtectedNameCache();
392
		
393
		
394
		return isParsable;
395
	}
396

    
397
	/**
398
	 * Parse the text and calculate errors
399
	 */
400
	public void parseAndCalculateErrors(){
401
		removeListener();
402
		String unparsedNameString = getNameViewer().getTextWidget().getText();
403
		parseHandler.parse(unparsedNameString);
404
		addListener();
405
		calculateErrors();
406
	}
407

    
408
	/**
409
	 * <p>getTaxonBase</p>
410
	 *
411
	 * @return the taxonBase
412
	 */
413
	public T getTaxonBase() {
414
		return getData();
415
	}
416
	
417
	/**
418
	 * <p>getName</p>
419
	 *
420
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
421
	 */
422
	public TaxonNameBase getName(){
423
		return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase().getName());
424
	}
425

    
426
	/**
427
	 * <p>persistName</p>
428
	 */
429
	public void persistName(){
430
		if(isDirty()){
431
			getNameViewer().getTextWidget().setEnabled(false);
432
			final String unparsedNameString = getNameViewer().getTextWidget().getText();
433
//			Job job = new Job("Persisting Name"){
434
//
435
//				@Override
436
//				protected IStatus run(IProgressMonitor monitor) {
437
//					
438
					final NonViralName name = parseHandler.parseAndResolveDuplicates(unparsedNameString);
439
//					
440
//					Display.getDefault().asyncExec(new Runnable(){
441
//						public void run() {
442
							getTaxonBase().setName(name);
443
							getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
444
							setDirty(false);
445
							getNameViewer().getTextWidget().setEnabled(true);							
446
//						};
447
//					});
448
//					
449
//					
450
//					return Status.OK_STATUS;
451
//				}
452
//				
453
//			};
454
//			
455
//			job.setPriority(Job.DECORATE);
456
//			job.schedule();
457
		}
458
	}
459
	
460
	/**
461
	 * <p>Getter for the field <code>group</code>.</p>
462
	 *
463
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
464
	 */
465
	public AbstractGroup getGroup(){
466
		if(group == null){
467
			throw new IllegalStateException("Group shall not be null.");
468
		}
469
		return group;
470
	}
471
	
472
	/**
473
	 * <p>remove</p>
474
	 */
475
	public void remove(){
476
		getGroup().remove(this);
477
	}
478
		
479
	/**
480
	 * <p>createControl</p>
481
	 */
482
	protected void createControl() {
483
		control = getEditor().getToolkit().createComposite(getGroup().getControl());
484
		
485
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
486
		TableWrapLayout layout = new TableWrapLayout();
487
		layout.leftMargin = 0;
488
		layout.rightMargin = 0;
489
		layout.topMargin = 5;
490
		layout.bottomMargin = 5;
491
		
492
		layout.verticalSpacing = 0;
493
		layout.horizontalSpacing = 0;
494
		
495
		control.setLayout(layout);
496
		
497
	}
498
	
499
	/**
500
	 * @return
501
	 */
502
	protected TaxonNameEditor getEditor() {
503
		return getGroup().getEditor();
504
	}
505

    
506
	/**
507
	 * <p>Getter for the field <code>control</code>.</p>
508
	 *
509
	 * @return a {@link org.eclipse.swt.widgets.Composite} object.
510
	 */
511
	public Composite getControl(){
512
		return control;
513
	}
514
	
515
	/**
516
	 * <p>createLineWrapSupport</p>
517
	 */
518
	protected void createLineWrapSupport() {
519
		new LineWrapSupport(getNameViewer(), getEditor().getManagedForm());
520
	}
521
	
522
	
523
	/**
524
	 * <p>createTextViewer</p>
525
	 */
526
	protected void createTextViewer() {
527
		nameViewer = new NameViewer(control);
528
		
529
		focusListener = new FocusAdapter() {
530
			public void focusGained(FocusEvent e) {
531
				for(AbstractGroupedContainer container : getEditor().getGroupedContainers()){
532
					container.colorSelected(NOT_SELECTED);
533
				}
534
				getEditor().getManagedForm().setInput(AbstractGroupedContainer.this);
535
				placeCursor();
536
				colorSelected(SELECTED_FOCUS);
537
			}
538
		};
539
		nameViewer.getTextWidget().addFocusListener(focusListener);
540
		
541
//		
542
		MouseAdapter mouseListener = new MouseAdapter() {
543
			public void mouseDown(MouseEvent e) {
544
				storeCursor();
545
			}
546
		};
547
		control.addMouseListener(mouseListener);
548
		nameViewer.getRulerControl().addMouseListener(mouseListener);
549
		nameViewer.getTextWidget().addMouseListener(mouseListener);
550
	}
551
	
552
	/**
553
	 * <p>setIcon</p>
554
	 *
555
	 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
556
	 */
557
	public void setIcon(Image icon) {
558
		getNameViewer().setIcon(icon);
559
	}
560
	
561
	/**
562
	 * <p>setIndent</p>
563
	 *
564
	 * @param indent a int.
565
	 */
566
	public void setIndent(int indent) {
567
		if (control.getLayout() instanceof TableWrapLayout) {
568
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
569
			layout.leftMargin = indent;
570
			layout.rightMargin = ACCEPTED_INDENT;
571
			control.setLayout(layout);
572
			control.layout();
573
		} else {
574
			new RuntimeException("Couldn't indent - composite's layout must be TableWrapLayout.");
575
		}
576
	}	
577
	
578
	/**
579
	 * <p>setSelected</p>
580
	 */
581
	public void setSelected() {
582
		getNameViewer().getTextWidget().setFocus();
583
	}
584
	
585
	/**
586
	 * <p>isSelected</p>
587
	 *
588
	 * @return a boolean.
589
	 */
590
	public boolean isSelected(){
591
		return getEditor().getSelectedContainer() == this;
592
	}
593
	
594
	/**
595
	 * <p>colorSelected</p>
596
	 *
597
	 * @param mode a int.
598
	 */
599
	public void colorSelected(int mode){
600
		if(!control.isDisposed()){
601
			String colorString = null;
602
			
603
			switch(mode){
604
			case SELECTED_FOCUS:
605
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
606
				break;
607
			case SELECTED_NO_FOCUS:
608
				colorString = Resources.COLOR_CONTROL_SELECTED;
609
				break;
610
			default:
611
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
612
			}
613
			
614
			backgroundColor = EditorUtil.getColor(colorString);
615
			
616
			setBackground(backgroundColor);
617
		}
618
	}
619

    
620
	
621
	/**
622
	 * <p>setDelayedSelection</p>
623
	 */
624
	protected void setDelayedSelection(){
625
		//TODO this might be done better
626
		// this is the quickest solution i could come up with and it improves performance
627
		// please reimplement if you know better.
628
		selection = this;
629
		
630
		// start timer
631
		Display display = Display.getCurrent();
632
		Runnable runnable = new Runnable() {
633
			
634
			public void run() {
635
				getEditor().getManagedForm().setInput(selection);
636
			}
637
		};
638
		display.timerExec(1000, runnable);
639
		
640
	}
641
	
642
	/**
643
	 * <p>setBackground</p>
644
	 *
645
	 * @param color a {@link org.eclipse.swt.graphics.Color} object.
646
	 */
647
	public void setBackground(Color color) {
648
		control.setBackground(color);
649
		
650
		for(Control child : control.getChildren()){
651
			child.setBackground(color);
652
		}
653
		
654
		getNameViewer().setBackground(color);
655
	}
656

    
657
	/* (non-Javadoc)
658
	 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
659
	 */
660
	/**
661
	 * <p>setFont</p>
662
	 *
663
	 * @param font a {@link org.eclipse.swt.graphics.Font} object.
664
	 */
665
	public void setFont(Font font) {
666
		getNameViewer().getTextWidget().setFont(font);
667
	}
668
	
669
	/**
670
	 * <p>Getter for the field <code>nameViewer</code>.</p>
671
	 *
672
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer} object.
673
	 */
674
	public NameViewer getNameViewer() {
675
		if (nameViewer == null){
676
			throw new RuntimeException("The Name Viewer is corrupt for Name Container: " + getTaxonBase().getName().getTitleCache());
677
		}
678
		return nameViewer;
679
	}
680
	
681
	/**
682
	 * If <code>textViewer</code> has already been set, it will show a
683
	 * <code>prompt</code> along the lines of "Click here to start entering data"
684
	 * when empty.
685
	 *
686
	 * @param prompt a {@link java.lang.String} object.
687
	 */
688
	public void createEmptyViewerPrompt(final String prompt) {
689
		
690
		Assert.isNotNull(getNameViewer());
691
		 
692
		final StyledText textControl = getNameViewer().getTextWidget();
693
		final IDocument document = getNameViewer().getDocument();
694
		
695
		setFocusListener(new FocusListener() {
696

    
697
			
698
			public void focusGained(FocusEvent e) {
699
				if (document.get().equals(prompt)) {
700
					textControl.setFont(getViewerFont());
701
					document.set("");
702
				}
703
			}
704

    
705
			
706
			public void focusLost(FocusEvent e) {
707
				if (document.getLength() == 0) {
708
					initEmptyText();
709
				}
710
			}
711
			
712
		});
713
		textControl.addFocusListener(getFocusListener());
714
		
715
		if (document.getLength() == 0) {
716
			textControl.setFont(EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT));
717
			document.set(prompt);
718
		}
719
	}
720
	
721
	/**
722
	 * <p>getViewerFont</p>
723
	 *
724
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
725
	 */
726
	abstract protected Font getViewerFont();
727

    
728
	/**
729
	 * <p>initEmptyText</p>
730
	 */
731
	protected void initEmptyText() {
732
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
733
		getNameViewer().getTextWidget().setFont(defaultFont);
734
		
735
		getNameViewer().getDocument().set(getEmptyTextPrompt());
736
		placeCursor();
737
	}
738

    
739
	/**
740
	 * <p>Setter for the field <code>focusListener</code>.</p>
741
	 *
742
	 * @param focusListener a {@link org.eclipse.swt.events.FocusListener} object.
743
	 */
744
	protected void setFocusListener(FocusListener focusListener) {
745
		this.focusListener = focusListener;
746
	}
747

    
748
	private FocusListener getFocusListener() {
749
		return focusListener;
750
	}
751

    
752
	/**
753
	 * <p>setDirty</p>
754
	 *
755
	 * @param isDirty a boolean.
756
	 */
757
	public void setDirty(boolean isDirty) {
758
		if(isDirty){
759
			getEditor().getManagedForm().dirtyStateChanged();
760
		}
761
		this.isDirty = isDirty;
762
	}
763
	
764
	/**
765
	 * <p>isDirty</p>
766
	 *
767
	 * @return a boolean.
768
	 */
769
	public boolean isDirty(){
770
		return isDirty;
771
	}
772
	
773
	/**
774
	 * <p>setMenu</p>
775
	 *
776
	 * @param menu a {@link org.eclipse.swt.widgets.Menu} object.
777
	 */
778
	public void setMenu (Menu menu) {
779
		control.setMenu(menu);
780
		
781
		getNameViewer().setMenu(menu);
782
	}
783
	
784
	private Control[] draggableControls;
785
	
786
	/**
787
	 * <p>setDraggableControl</p>
788
	 *
789
	 * @param controls an array of {@link org.eclipse.swt.widgets.Control} objects.
790
	 */
791
	protected void setDraggableControl(Control[] controls) {
792
		draggableControls = controls;
793
	}
794
	
795
	/**
796
	 * <p>setIsDraggable</p>
797
	 *
798
	 * @param draggable a boolean.
799
	 */
800
	public void setIsDraggable(boolean draggable) {
801

    
802
		if (draggable) {
803

    
804
			if (draggableControls == null) {
805
				throw new NullPointerException(
806
						"Draggable controls must be set to add draggability");
807
			}
808
			
809
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };			
810
			int operations = DND.DROP_MOVE;
811

    
812
			for(Control draggableControl : draggableControls){
813
				DragSource dragSource = new DragSource(draggableControl, operations);
814
				dragSource.setTransfer(types);
815
				
816
				dragSource.addDragListener(new NameEditorDragListener(this));
817
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(control));
818
			}
819
		} 
820
	}
821

    
822
	private String nonEditableText;
823

    
824
	ControlListener nonEditableResizeListener = new ControlAdapter() {
825
		
826
		int width = 0;
827
		
828
		public void controlResized(ControlEvent e) {
829
			if (nonEditableInfoLabel.getBounds().width == width) {
830
				return;
831
			}
832
			width = nonEditableInfoLabel.getBounds().width;
833
			if (nonEditableInfoLabel.getBounds().width > 0) {
834
				nonEditableInfoLabel.setText(
835
						Dialog.shortenText(nonEditableText.toUpperCase(), 
836
						nonEditableInfoLabel));
837
			}
838
		}
839
	};
840

    
841
	private String nonEditableHoverText;
842

    
843
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
844

    
845
	private T data;
846
			
847
	/**
848
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
849
	 * input field. For instance, NameComposites display things like name relations,
850
	 * sec. references, etc. here.
851
	 *
852
	 * @param info the text to display in the label
853
	 * @param append whether the string should be appended to text that is already shown in the label
854
	 */
855
	public void setNonEditableInfo(String info, boolean append) {
856
		// TODO non editable info should only be drawn once, when everything else is drawn
857
		info = info.toUpperCase();
858
		
859
		if(append){
860
			nonEditableText += ", " + info;
861
			nonEditableHoverText += "\n" + info;
862
		}else{
863
			nonEditableText = info;
864
			nonEditableHoverText = info;
865
		}
866
		
867
		if (nonEditableInfoLabel == null) {
868
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(control, "");
869
			TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP);
870
			// Set indent to viewer ruler's width 
871
			if (getNameViewer().getRulerControl() != null) {
872
				// TODO right justify
873
				layoutData.indent = NameViewer.RULER_WIDTH;
874
			}
875
			nonEditableInfoLabel.setLayoutData(layoutData);
876
			
877

    
878
			
879
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(nonEditableInfoLabel) {
880
				@Override
881
				public String getLabelText() {
882
					return nonEditableText.toUpperCase();
883
				}
884
			}; 
885
			nonEditableInfoLabel.addControlListener(nonEditableLabelEllipsisListener);
886
			
887
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
888
			nonEditableInfoHover.setRespectDisplayBounds(true);
889
			
890
		} 
891
		nonEditableInfoHover.setText(nonEditableHoverText);
892
		nonEditableInfoLabel.setText(nonEditableText);
893
	}
894
	
895
	/**
896
	 * <p>Getter for the field <code>data</code>.</p>
897
	 *
898
	 * @return a T object.
899
	 */
900
	public T getData(){
901
		return data;
902
	}
903
	
904
	/**
905
	 * <p>Setter for the field <code>data</code>.</p>
906
	 *
907
	 * @param data a T object.
908
	 */
909
	public void setData(T data){
910
		this.data =  (T) HibernateProxyHelper.deproxy(data);
911
	}
912

    
913
	/**
914
	 * If the user hitting carriage return should cause something to happen -
915
	 * i.e. the creation of a new composite - call this method and override
916
	 * the method handleSplitText().
917
	 */
918
	protected void createLineBreakListener() {
919
		lineBreakListener = new LineBreakListener() {
920
			@Override
921
			public void handleSplitText(String text) {
922
				AbstractGroupedContainer.this.handleSplitText(text);	
923
			}
924
		};
925
		
926
		getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
927
	}
928
		
929
	abstract class LabelEllipsisListener extends ControlAdapter {
930
		
931
		private Label label;
932
		int width = 0;
933

    
934
		LabelEllipsisListener(Label label) {
935
			this.label = label;
936
		}
937
		
938
		abstract public String getLabelText();
939
		
940
		public void controlResized(ControlEvent e) {
941
			if (label.getBounds().width == width) {
942
				return;
943
			}
944
			width = label.getBounds().width;
945
			if (label.getBounds().width > 0) {
946
				label.setText(TextHelper.shortenText(getLabelText(), label));
947
			}
948
		}
949
	}
950

    
951
	/**
952
	 * <p>storeCursor</p>
953
	 */
954
	public void storeCursor() {
955
		this.cursorPosition = getNameViewer().getCursorPosition();
956
	}
957
	
958
	/**
959
	 * Puts the cursor to the position it was last seen on or to the end of line
960
	 * if no former position is known.
961
	 */
962
	public void placeCursor(){
963
		if(cursorPosition == 0){
964
			getNameViewer().setCursorToEOL();
965
		}else{
966
			getNameViewer().setCursorPosition(cursorPosition);
967
		}
968
	}
969

    
970

    
971

    
972
	/**
973
	 * <p>Setter for the field <code>group</code>.</p>
974
	 *
975
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
976
	 */
977
	public void setGroup(AbstractGroup group) {
978
		this.group = group;
979
	}
980

    
981

    
982

    
983
	/**
984
	 * <p>restoreColor</p>
985
	 */
986
	public void restoreColor() {
987
		setBackground(backgroundColor);
988
	}
989
	
990

    
991

    
992
	/* (non-Javadoc)
993
	 * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
994
	 */
995
	@Override
996
	public void initialize(IManagedForm form) {
997
		// TODO Auto-generated method stub
998
		
999
	}
1000

    
1001

    
1002
	/* (non-Javadoc)
1003
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1004
	 */
1005
	@Override
1006
	public void dispose() {
1007
		if(getControl() != null){
1008
			setMenu(null);
1009
			getControl().dispose();
1010
		}
1011
	}
1012

    
1013

    
1014
	/* (non-Javadoc)
1015
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1016
	 */
1017
	@Override
1018
	public void commit(boolean onSave) {
1019
		if(isDirty()){
1020
			persistName();
1021
		}
1022
	}
1023

    
1024

    
1025
	/* (non-Javadoc)
1026
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1027
	 */
1028
	@Override
1029
	public boolean setFormInput(Object input) {
1030
		// TODO Auto-generated method stub
1031
		return false;
1032
	}
1033

    
1034

    
1035
	/* (non-Javadoc)
1036
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1037
	 */
1038
	@Override
1039
	public void setFocus() {
1040
		getNameViewer().getControl().setFocus();
1041
	}
1042

    
1043

    
1044
	/* (non-Javadoc)
1045
	 * @see org.eclipse.ui.forms.IFormPart#isStale()
1046
	 */
1047
	@Override
1048
	public boolean isStale() {
1049
		// TODO Auto-generated method stub
1050
		return false;
1051
	}
1052
} 
(2-2/19)