Project

General

Profile

Download (27.9 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.List;
13
import java.util.Set;
14

    
15
import javax.naming.OperationNotSupportedException;
16

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

    
47
import eu.etaxonomy.cdm.common.CdmUtils;
48
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
49
import eu.etaxonomy.cdm.model.name.NameRelationship;
50
import eu.etaxonomy.cdm.model.name.NonViralName;
51
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
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.EditorUtil;
56
import eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor;
57
import eu.etaxonomy.taxeditor.editor.name.container.EditorAnnotation.EditorAnnotationType;
58
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragListener;
59
import eu.etaxonomy.taxeditor.editor.name.dnd.NameEditorDragSourceEffect;
60
import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
61
import eu.etaxonomy.taxeditor.editor.singlesource.name.container.INameViewer;
62
import eu.etaxonomy.taxeditor.editor.singlesource.name.container.LineWrapSupportFacade;
63
import eu.etaxonomy.taxeditor.editor.singlesource.name.container.NameViewerFacade;
64
import eu.etaxonomy.taxeditor.labels.ILabelImageStrategy;
65
import eu.etaxonomy.taxeditor.labels.LabelImageProvider;
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
import eu.etaxonomy.taxeditor.store.singlesource.widget.DisplayProxy;
72

    
73
/**
74
 * Formats <code>GroupedComposite</code> with cosmetic and layout properties specific to the
75
 * Editor. This should be used to maintain a consistent look and feel for all Editor
76
 * freetext area components, such as DescriptionElementComposite.
77
 * <p>
78
 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the contents
79
 * of {@link #getData()} when the <code>GroupedComposite</code> gets focus, i.e. to
80
 * 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> in its
84
 * own <code>getData()</code>.
85
 * </p>
86
 * <p>
87
 * The <code>IManagedForm</code> can also used for drawing borders by calling the method
88
 * <code>createBorderSupport()</code>.
89
 * </p>
90
 *
91
 * @author p.ciardelli
92
 * @author n.hoffmann
93
 * @created 02.06.2008
94
 * @version 1.0
95
 */
96
abstract public class AbstractGroupedContainer<T extends TaxonBase> implements IFormPart, IContainerConstants, IElementHasDetails {	
97

    
98
	protected ParseHandler parseHandler;
99
	
100
	private FocusListener nameCompositeFocusListener;
101
	private ModifyListener nameCompositeModifyListener;
102

    
103
	protected NameViewerFacade nameViewer;
104
	
105
	private AbstractGroup group;
106
	
107
	private Label nonEditableInfoLabel;
108
	private DefaultToolTip nonEditableInfoHover;
109
	
110
	private static AbstractGroupedContainer 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
	
123
	/**
124
	 * <p>Constructor for AbstractGroupedContainer.</p>
125
	 *
126
	 * @param editor a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor} object.
127
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
128
	 * @param taxonBase a T object.
129
	 * @param <T> a T object.
130
	 */
131
	public AbstractGroupedContainer(T taxonBase){
132
		setData(taxonBase);
133
		parseHandler = ParseHandler.NewInstance(taxonBase.getName());
134
	}
135
	
136
	public void createContent(){
137
		createControl();
138
		
139
		try {
140
			createTextViewer();
141
			createLineWrapSupport();
142
		} catch (OperationNotSupportedException e) {
143
			// TODO Auto-generated catch block
144
			e.printStackTrace();
145
		}
146
		createLineBreakListener();
147

    
148
		setMenu(getEditor().getMenu());
149
		
150
		setDraggableControl(new Control[]{getControl(), getNameViewer().getDraggableControl()});
151
		
152
		createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
153
		
154
		
155
		initializeComposite();
156
		
157
		createListener();
158

    
159
		isFreeTextEnabled();
160
	}
161

    
162
	
163
	
164
	/**
165
	 * <p>createListener</p>
166
	 */
167
	protected void createListener(){
168
		nameCompositeModifyListener = new ModifyListener(){
169
			
170
			public void modifyText(ModifyEvent e) {
171
				// mark the composite dirty
172
				setDirty(true);
173
				// parse the text
174
				String text = nameViewer.getText();
175
				
176
				NonViralName name = parseHandler.parse(text);
177
				getTaxonBase().setName(name);
178
				getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
179
				
180
				// show errors resulting from parsing
181
				calculateAnnotations();
182
				// store the position of the cursor				
183
				storeCursor();
184
				// notify selection listener
185
				setDelayedSelection();
186
			}
187
		};
188
		nameCompositeFocusListener = new FocusAdapter(){
189

    
190
			/* (non-Javadoc)
191
			 * @see org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt.events.FocusEvent)
192
			 */
193
			@Override
194
			public void focusLost(FocusEvent e) {
195
				super.focusLost(e);
196

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

    
292
	/**
293
	 * 
294
	 */
295
	public void showAnnotations() {
296
		
297
		if(getName().hasProblem()){
298
			showParsingProblems();
299
		}
300
		
301
		if(!isNameParsable()){
302
			getNameViewer().addAnnotation(
303
					new EditorAnnotation(EditorAnnotationType.WARNING, 0, "This name may only be edited in the details view."));
304
		}
305
		
306
		if(isNameUsedMultipleTimes()){
307
			getNameViewer().addAnnotation(new EditorAnnotation(EditorAnnotationType.WARNING, 0, "This taxons name is used multiple times."));
308
		}
309
		
310
		
311
	}
312

    
313
	/**
314
	 * 
315
	 */
316
	private void showParsingProblems() {
317
		String text = getNameViewer().getText();
318
		
319
		List<ParserProblem> parsingProblems = getName().getParsingProblems();
320
		
321
		for (ParserProblem problem : parsingProblems) {
322
			getNameViewer().addAnnotation(new EditorAnnotation(problem), getParsingProblemPosition());
323
		}	
324
	}
325
	
326
	private Position getParsingProblemPosition(){
327
		String text = getNameViewer().getText();
328
		
329
		if (getName().hasProblem() && text.length() > 0) {
330
			int start = getName().getProblemStarts();
331
			int length = getName().getProblemEnds() - start;
332
			
333
			if (start == -1 || getName().getProblemEnds() == -1) {
334
				return null;
335
			}
336
			
337
			// Don't let squigglies try to draw beyond the end of the text
338
			if (text.length() < start + length) {
339
				length = text.length() - start;
340
			}
341
			
342
			return new Position(start, length);
343
		}
344
		return null;
345
	}
346

    
347
	/**
348
	 * <p>handleSplitText</p>
349
	 *
350
	 * @param text a {@link java.lang.String} object.
351
	 */
352
	protected void handleSplitText(String text) {
353
		// Create a synonym in a new homotypic group using text as name
354
		TaxonNameBase synonymName = ParseHandler.parseReferencedName(text, null);
355
		
356
		EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation
357
				("New Heterotypic Synonym", getEditor().getUndoContext(), getEditor().getTaxon(), synonymName, getEditor()));
358
	}
359

    
360
	/**
361
	 * Refreshes the display with latest data from the model.
362
	 *
363
	 * Note: Will not parse the text and not calculate errors!
364
	 */
365
	public void refresh() {
366
//		showNameRelations();
367
		
368
		String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
369

    
370
/* RAP SS-Exclude, obviously not needed		
371
//		if(getNameViewer().getTextWidget() == null){
372
//			// we might get here via dnd. Look slike it can be ignored
373
//			return;
374
//		}
375
*/
376
		
377
		if (text.length() == 0) {
378
			initEmptyText();
379
		} else if(! getNameViewer().getText().equals(text)) {
380
			removeListener();
381
			getNameViewer().setText(text);
382
			addListener();
383
		}
384
		
385
		updateNonEditableInfo();
386
		
387
		updateIcon();
388
//		placeCursor();
389
		updateIndent();
390
		
391
		isFreeTextEnabled();
392
	}
393

    
394
	/**
395
	 * 
396
	 */
397
	protected abstract void updateIcon();
398

    
399
	
400
	protected abstract void updateIndent();
401

    
402

    
403
	/**
404
	 * <p>updateNonEditableInfo</p>
405
	 */
406
	protected abstract void updateNonEditableInfo();
407
	
408
	/**
409
	 * 
410
	 */
411
	private void isFreeTextEnabled() {
412
		// Decide whether editing of freetext is allowed or not.
413
		if (freetextEditingAllowed()) {
414
			
415
			// set editable
416
			getNameViewer().setEditable(true);
417
			
418
			// restore foreground font color again
419
			getNameViewer().setForeground(control.getForeground());
420
		} else {
421
			// set non-editable
422
			getNameViewer().setEditable(false);
423
			
424
			// grey out text as a non-editable indicator
425
			getNameViewer().setForeground(EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR));
426
		}
427
	}
428
	
429

    
430
	/**
431
	 * Checks whether the freetext should be editable based on specific empty fields.
432
	 * @return
433
	 */
434
	private boolean freetextEditingAllowed() {
435
		NonViralName name = (NonViralName) HibernateProxyHelper.deproxy(getName());
436
		boolean enableFreetext = true;
437

    
438
		enableFreetext |= isNameUsedMultipleTimes();
439
		enableFreetext &= isNameParsable();
440
		
441
		return enableFreetext;
442
	}
443
	
444
	private boolean isNameUsedMultipleTimes(){
445
		if(getName().getTaxonBases().size() > 1){
446
			return true;
447
		}
448
		return false;
449
	}
450
	
451
	private boolean isNameParsable(){
452
		TaxonNameBase name = getName();
453
		
454
		boolean isParsable = true; 
455
		isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); //taxonFieldsEmpty();
456
		
457
		if(name instanceof NonViralName){
458
			NonViralName nonViralName = (NonViralName) name;
459
			isParsable &= ! nonViralName.isProtectedAuthorshipCache();
460
			isParsable &= ! nonViralName.isProtectedNameCache();
461
		}
462
		
463
		return isParsable;
464
	}
465

    
466
	/**
467
	 * Parse the text and calculate errors
468
	 */
469
	public void parseAndCalculateAnnotations(){
470
		removeListener();
471
		String unparsedNameString = getNameViewer().getText();
472
		parseHandler.parse(unparsedNameString);
473
		addListener();
474
		calculateAnnotations();
475
	}
476

    
477
	/**
478
	 * <p>getTaxonBase</p>
479
	 *
480
	 * @return the taxonBase
481
	 */
482
	public T getTaxonBase() {
483
		return getData();
484
	}
485
	
486
	/**
487
	 * <p>getName</p>
488
	 *
489
	 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
490
	 */
491
	public TaxonNameBase getName(){
492
		return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase().getName());
493
	}
494

    
495
	/**
496
	 * <p>persistName</p>
497
	 */
498
	public void persistName(){
499
		if(isDirty()){
500
			getNameViewer().setEnabled(false);
501
			final String unparsedNameString = getNameViewer().getText();
502
//			Job job = new Job("Persisting Name"){
503
//
504
//				@Override
505
//				protected IStatus run(IProgressMonitor monitor) {
506
//					
507
					final NonViralName name = parseHandler.parseAndResolveDuplicates(unparsedNameString);
508
//					
509
//					Display.getDefault().asyncExec(new Runnable(){
510
//						public void run() {
511
							getTaxonBase().setName(name);
512
							getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
513
							setDirty(false);
514
							getNameViewer().setEnabled(true);							
515
//						};
516
//					});
517
//					
518
//					
519
//					return Status.OK_STATUS;
520
//				}
521
//				
522
//			};
523
//			
524
//			job.setPriority(Job.DECORATE);
525
//			job.schedule();
526
		}
527
	}
528
	
529
	/**
530
	 * <p>Getter for the field <code>group</code>.</p>
531
	 *
532
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
533
	 */
534
	public AbstractGroup getGroup(){
535
		if(group == null){
536
			throw new IllegalStateException("Group shall not be null.");
537
		}
538
		return group;
539
	}
540
	
541
	/**
542
	 * <p>remove</p>
543
	 */
544
	public void remove(){
545
		getGroup().remove(this);
546
	}
547
		
548
	/**
549
	 * <p>createControl</p>
550
	 */
551
	protected void createControl() {
552
		control = getEditor().getToolkit().createComposite(getGroup().getControl());
553
		
554
		control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
555
		TableWrapLayout layout = new TableWrapLayout();
556
		layout.leftMargin = 0;
557
		layout.rightMargin = 0;
558
		layout.topMargin = 5;
559
		layout.bottomMargin = 5;
560
		
561
		layout.verticalSpacing = 0;
562
		layout.horizontalSpacing = 0;
563
		
564
		control.setLayout(layout);
565
		
566
	}
567
	
568
	/**
569
	 * @return
570
	 */
571
	protected TaxonNameEditor getEditor() {
572
		return getGroup().getEditor();
573
	}
574

    
575
	/**
576
	 * <p>Getter for the field <code>control</code>.</p>
577
	 *
578
	 * @return a {@link org.eclipse.swt.widgets.Composite} object.
579
	 */
580
	public Composite getControl(){
581
		return control;
582
	}
583
	
584
	/**
585
	 * <p>createLineWrapSupport</p>
586
	 * @throws OperationNotSupportedException 
587
	 */
588
	protected void createLineWrapSupport() throws OperationNotSupportedException {
589
		new LineWrapSupportFacade(getNameViewer(), getEditor().getManagedForm());
590
	}
591
	
592
	
593
	/**
594
	 * <p>createTextViewer</p>
595
	 * @throws OperationNotSupportedException 
596
	 */
597
	protected void createTextViewer() throws OperationNotSupportedException {
598
		nameViewer = new NameViewerFacade(control);
599
		
600
		focusListener = new FocusAdapter() {
601
			@Override
602
			public void focusGained(FocusEvent e) {
603
				for(AbstractGroupedContainer container : getEditor().getGroupedContainers()){
604
					container.colorSelected(NOT_SELECTED);
605
				}
606
				getEditor().getManagedForm().setInput(AbstractGroupedContainer.this);
607
				placeCursor();
608
				colorSelected(SELECTED_FOCUS);
609
			}
610
		};
611
		nameViewer.addFocusListener(focusListener);
612
		
613
//		
614
		MouseAdapter mouseListener = new MouseAdapter() {
615
			@Override
616
			public void mouseDown(MouseEvent e) {
617
				storeCursor();
618
			}
619
		};
620
		control.addMouseListener(mouseListener);
621
		nameViewer.addMouseListener(mouseListener);
622
	}
623
	
624
	/**
625
	 * <p>setIcon</p>
626
	 *
627
	 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
628
	 */
629
	public void setIcon(Image icon) {
630
		getNameViewer().setIcon(icon);
631
	}
632
	
633
	/**
634
	 * <p>setIndent</p>
635
	 *
636
	 * @param indent a int.
637
	 */
638
	public void setIndent(int indent) {
639
		if (control.getLayout() instanceof TableWrapLayout) {
640
			TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
641
			layout.leftMargin = indent;
642
			layout.rightMargin = ACCEPTED_INDENT;
643
			control.setLayout(layout);
644
			control.layout();
645
		} else {
646
			new RuntimeException("Couldn't indent - composite's layout must be TableWrapLayout.");
647
		}
648
	}	
649
	
650
	/**
651
	 * <p>setSelected</p>
652
	 */
653
	public void setSelected() {
654
		getNameViewer().setFocus();
655
	}
656
	
657
	/**
658
	 * <p>isSelected</p>
659
	 *
660
	 * @return a boolean.
661
	 */
662
	public boolean isSelected(){
663
		return getEditor().getSelectedContainer() == this;
664
	}
665
	
666
	/**
667
	 * <p>colorSelected</p>
668
	 *
669
	 * @param mode a int.
670
	 */
671
	public void colorSelected(int mode){
672
		if(!control.isDisposed()){
673
			String colorString = null;
674
			
675
			switch(mode){
676
			case SELECTED_FOCUS:
677
				colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
678
				break;
679
			case SELECTED_NO_FOCUS:
680
				colorString = Resources.COLOR_CONTROL_SELECTED;
681
				break;
682
			default:
683
				colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
684
			}
685
			
686
			backgroundColor = EditorUtil.getColor(colorString);
687
			
688
			setBackground(backgroundColor);
689
		}
690
	}
691

    
692
	
693
	/**
694
	 * <p>setDelayedSelection</p>
695
	 */
696
	protected void setDelayedSelection(){
697
		//TODO this might be done better
698
		// this is the quickest solution i could come up with and it improves performance
699
		// please reimplement if you know better.
700
		selection = this;
701
		
702
		// start timer
703
		Display display = DisplayProxy.getDefault();
704
		Runnable runnable = new Runnable() {
705
			
706
			public void run() {
707
				getEditor().getManagedForm().setInput(selection);
708
			}
709
		};
710
		display.timerExec(1000, runnable);
711
		
712
	}
713
	
714
	/**
715
	 * <p>setBackground</p>
716
	 *
717
	 * @param color a {@link org.eclipse.swt.graphics.Color} object.
718
	 */
719
	public void setBackground(Color color) {
720
		control.setBackground(color);
721
		
722
		for(Control child : control.getChildren()){
723
			child.setBackground(color);
724
		}
725
		
726
		getNameViewer().setBackground(color);
727
	}
728

    
729
	/* (non-Javadoc)
730
	 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
731
	 */
732
	/**
733
	 * <p>setFont</p>
734
	 *
735
	 * @param font a {@link org.eclipse.swt.graphics.Font} object.
736
	 */
737
	public void setFont(Font font) {
738
		getNameViewer().setFont(font);
739
	}
740
	
741
	/**
742
	 * <p>Getter for the field <code>nameViewer</code>.</p>
743
	 *
744
	 * @return a {@link eu.etaxonomy.taxeditor.editor.name.container.NameViewer} object.
745
	 */
746
	public NameViewerFacade getNameViewer() {
747
		if (nameViewer == null){
748
			throw new RuntimeException("The Name Viewer is corrupt for Name Container: " + getTaxonBase().getName().getTitleCache());
749
		}
750
		return nameViewer;
751
	}
752
	
753
	/**
754
	 * If <code>textViewer</code> has already been set, it will show a
755
	 * <code>prompt</code> along the lines of "Click here to start entering data"
756
	 * when empty.
757
	 *
758
	 * @param prompt a {@link java.lang.String} object.
759
	 */
760
	public void createEmptyViewerPrompt(final String prompt) {
761
		
762
		Assert.isNotNull(getNameViewer());
763
		 
764
		setFocusListener(new FocusListener() {
765

    
766
			
767
			public void focusGained(FocusEvent e) {
768
				try {
769
					if (getNameViewer().getDocumentAsString().equals(prompt)) {
770
						getNameViewer().setFont(getViewerFont());
771
						getNameViewer().setDocumentAsString("");
772
					}
773
				} catch (OperationNotSupportedException e1) {
774
				}
775
			}
776

    
777
			
778
			public void focusLost(FocusEvent e) {
779
				try {
780
					if (getNameViewer().getDocumentLength() == 0) {
781
						initEmptyText();
782
					}
783
				} catch (OperationNotSupportedException e1) {
784
				}
785
			}
786
			
787
		});
788
		getNameViewer().addFocusListener(getFocusListener());
789
		
790
		try {
791
			if (getNameViewer().getDocumentLength() == 0) {
792
				getNameViewer().setFont(EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT));
793
				getNameViewer().setDocumentAsString(prompt);
794
			}
795
		} catch (OperationNotSupportedException e1) {
796
		}
797
	}
798
	
799
	/**
800
	 * <p>getViewerFont</p>
801
	 *
802
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
803
	 */
804
	abstract protected Font getViewerFont();
805

    
806
	/**
807
	 * <p>initEmptyText</p>
808
	 */
809
	protected void initEmptyText() {
810
		Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
811
		getNameViewer().setFont(defaultFont);
812
		
813
		getNameViewer().setDocumentAsString(getEmptyTextPrompt());
814
		placeCursor();
815
	}
816

    
817
	/**
818
	 * <p>Setter for the field <code>focusListener</code>.</p>
819
	 *
820
	 * @param focusListener a {@link org.eclipse.swt.events.FocusListener} object.
821
	 */
822
	protected void setFocusListener(FocusListener focusListener) {
823
		this.focusListener = focusListener;
824
	}
825

    
826
	private FocusListener getFocusListener() {
827
		return focusListener;
828
	}
829

    
830
	/**
831
	 * <p>setDirty</p>
832
	 *
833
	 * @param isDirty a boolean.
834
	 */
835
	public void setDirty(boolean isDirty) {
836
		if(isDirty){
837
			getEditor().getManagedForm().dirtyStateChanged();
838
		}
839
		this.isDirty = isDirty;
840
	}
841
	
842
	/**
843
	 * <p>isDirty</p>
844
	 *
845
	 * @return a boolean.
846
	 */
847
	public boolean isDirty(){
848
		return isDirty;
849
	}
850
	
851
	/**
852
	 * <p>setMenu</p>
853
	 *
854
	 * @param menu a {@link org.eclipse.swt.widgets.Menu} object.
855
	 */
856
	public void setMenu (Menu menu) {
857
		control.setMenu(menu);
858
		
859
		getNameViewer().setMenu(menu);
860
	}
861
	
862
	private Control[] draggableControls;
863
	
864
	/**
865
	 * <p>setDraggableControl</p>
866
	 *
867
	 * @param controls an array of {@link org.eclipse.swt.widgets.Control} objects.
868
	 */
869
	protected void setDraggableControl(Control[] controls) {
870
		draggableControls = controls;
871
	}
872
	
873
	/**
874
	 * <p>setIsDraggable</p>
875
	 *
876
	 * @param draggable a boolean.
877
	 */
878
	public void setIsDraggable(boolean draggable) {
879

    
880
		if (draggable) {
881

    
882
			if (draggableControls == null) {
883
				throw new NullPointerException(
884
						"Draggable controls must be set to add draggability");
885
			}
886
			
887
			Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };			
888
			int operations = DND.DROP_MOVE;
889

    
890
			for(Control draggableControl : draggableControls){
891
				DragSource dragSource = new DragSource(draggableControl, operations);
892
				dragSource.setTransfer(types);
893
				
894
				dragSource.addDragListener(new NameEditorDragListener(this));
895
				dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(control));
896
			}
897
		} 
898
	}
899

    
900
	private String nonEditableText;
901

    
902
	ControlListener nonEditableResizeListener = new ControlAdapter() {
903
		
904
		int width = 0;
905
		
906
		@Override
907
		public void controlResized(ControlEvent e) {
908
			if (nonEditableInfoLabel.getBounds().width == width) {
909
				return;
910
			}
911
			width = nonEditableInfoLabel.getBounds().width;
912
			if (nonEditableInfoLabel.getBounds().width > 0) {
913
				nonEditableInfoLabel.setText(
914
						Dialog.shortenText(nonEditableText.toUpperCase(), 
915
						nonEditableInfoLabel));
916
			}
917
		}
918
	};
919

    
920
	private String nonEditableHoverText;
921

    
922
	private LabelEllipsisListener nonEditableLabelEllipsisListener;
923

    
924
	private T data;
925
			
926
	/**
927
	 * nonEditableInfo is a label displayed underneath a GroupedComposite's
928
	 * input field. For instance, NameComposites display things like name relations,
929
	 * sec. references, etc. here.
930
	 *
931
	 * @param info the text to display in the label
932
	 * @param append whether the string should be appended to text that is already shown in the label
933
	 * @throws OperationNotSupportedException 
934
	 */
935
	public void setNonEditableInfo(String info, boolean append) throws OperationNotSupportedException {
936
		// TODO non editable info should only be drawn once, when everything else is drawn
937
		info = info.toUpperCase();
938
		
939
		if(append){
940
			nonEditableText += ", " + info;
941
			nonEditableHoverText += "\n" + info;
942
		}else{
943
			nonEditableText = info;
944
			nonEditableHoverText = info;
945
		}
946
		
947
		if (nonEditableInfoLabel == null) {
948
			nonEditableInfoLabel = getEditor().getToolkit().createLabel(control, "");
949
			TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP);
950
			// Set indent to viewer ruler's width 
951
			if (getNameViewer().getVerticalRulerControl() != null) {
952
				// TODO right justify
953
				layoutData.indent = INameViewer.RULER_WIDTH;
954
			}
955
			nonEditableInfoLabel.setLayoutData(layoutData);
956
			
957

    
958
			
959
			nonEditableLabelEllipsisListener = new LabelEllipsisListener(nonEditableInfoLabel) {
960
				@Override
961
				public String getLabelText() {
962
					return nonEditableText.toUpperCase();
963
				}
964
			}; 
965
			nonEditableInfoLabel.addControlListener(nonEditableLabelEllipsisListener);
966
			
967
			nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
968
			nonEditableInfoHover.setRespectDisplayBounds(true);
969
			
970
		} 
971
		nonEditableInfoHover.setText(nonEditableHoverText);
972
		nonEditableInfoLabel.setText(nonEditableText);
973
		
974
		calculateAnnotations();
975
	}
976
	
977
	/**
978
	 * <p>Getter for the field <code>data</code>.</p>
979
	 *
980
	 * @return a T object.
981
	 */
982
	public T getData(){
983
		return data;
984
	}
985
	
986
	/**
987
	 * <p>Setter for the field <code>data</code>.</p>
988
	 *
989
	 * @param data a T object.
990
	 */
991
	public void setData(T data){
992
		this.data =  (T) HibernateProxyHelper.deproxy(data);
993
	}
994

    
995
	/**
996
	 * If the user hitting carriage return should cause something to happen -
997
	 * i.e. the creation of a new composite - call this method and override
998
	 * the method handleSplitText().
999
	 */
1000
	protected void createLineBreakListener() {
1001
		lineBreakListener = new LineBreakListener() {
1002
			@Override
1003
			public void handleSplitText(String text) {
1004
				AbstractGroupedContainer.this.handleSplitText(text);	
1005
			}
1006
		};
1007
		
1008
		getNameViewer().addVerifyListener(lineBreakListener);
1009
	}
1010
		
1011
	abstract class LabelEllipsisListener extends ControlAdapter {
1012
		
1013
		private final Label label;
1014
		int width = 0;
1015

    
1016
		LabelEllipsisListener(Label label) {
1017
			this.label = label;
1018
		}
1019
		
1020
		abstract public String getLabelText();
1021
		
1022
		@Override
1023
		public void controlResized(ControlEvent e) {
1024
			if (label.getBounds().width == width) {
1025
				return;
1026
			}
1027
			width = label.getBounds().width;
1028
			if (label.getBounds().width > 0) {
1029
				label.setText(TextHelper.shortenText(getLabelText(), label));
1030
			}
1031
		}
1032
	}
1033

    
1034
	/**
1035
	 * <p>storeCursor</p>
1036
	 */
1037
	public void storeCursor() {
1038
		try {
1039
			this.cursorPosition = getNameViewer().getCursorPosition();
1040
		} catch (OperationNotSupportedException e) {
1041
			this.cursorPosition = 0;
1042
		}
1043
	}
1044
	
1045
	/**
1046
	 * Puts the cursor to the position it was last seen on or to the end of line
1047
	 * if no former position is known.
1048
	 */
1049
	public void placeCursor(){
1050
		if(cursorPosition == 0){
1051
			getNameViewer().setCursorToEOL();
1052
		}else{
1053
			getNameViewer().setCursorPosition(cursorPosition);
1054
		}
1055
	}
1056

    
1057

    
1058

    
1059
	/**
1060
	 * <p>Setter for the field <code>group</code>.</p>
1061
	 *
1062
	 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.container.AbstractGroup} object.
1063
	 */
1064
	public void setGroup(AbstractGroup group) {
1065
		this.group = group;
1066
	}
1067

    
1068

    
1069

    
1070
	/**
1071
	 * <p>restoreColor</p>
1072
	 */
1073
	public void restoreColor() {
1074
		setBackground(backgroundColor);
1075
	}
1076
	
1077

    
1078

    
1079
	/* (non-Javadoc)
1080
	 * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
1081
	 */
1082
	@Override
1083
	public void initialize(IManagedForm form) {
1084
		// TODO Auto-generated method stub
1085
		
1086
	}
1087

    
1088

    
1089
	/* (non-Javadoc)
1090
	 * @see org.eclipse.ui.forms.IFormPart#dispose()
1091
	 */
1092
	@Override
1093
	public void dispose() {
1094
		if(getControl() != null){
1095
			setMenu(null);
1096
			getControl().dispose();
1097
		}
1098
	}
1099

    
1100

    
1101
	/* (non-Javadoc)
1102
	 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1103
	 */
1104
	@Override
1105
	public void commit(boolean onSave) {
1106
		if(isDirty()){
1107
			persistName();
1108
		}
1109
	}
1110

    
1111

    
1112
	/* (non-Javadoc)
1113
	 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1114
	 */
1115
	@Override
1116
	public boolean setFormInput(Object input) {
1117
		// TODO Auto-generated method stub
1118
		return false;
1119
	}
1120

    
1121

    
1122
	/* (non-Javadoc)
1123
	 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1124
	 */
1125
	@Override
1126
	public void setFocus() {
1127
		getNameViewer().setFocus();
1128
	}
1129

    
1130

    
1131
	/* (non-Javadoc)
1132
	 * @see org.eclipse.ui.forms.IFormPart#isStale()
1133
	 */
1134
	@Override
1135
	public boolean isStale() {
1136
		// TODO Auto-generated method stub
1137
		return false;
1138
	}
1139
} 
(2-2/19)