0befdbae6bd109333b0d94b8b763b1ffad6be359
[taxeditor.git] / taxeditor-editor / src / main / java / eu / etaxonomy / taxeditor / editor / name / AbstractGroupedContainer.java
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;
11
12 import java.lang.reflect.Method;
13 import java.util.Set;
14
15 import org.eclipse.core.runtime.Assert;
16 import org.eclipse.jface.dialogs.Dialog;
17 import org.eclipse.jface.text.IDocument;
18 import org.eclipse.jface.window.DefaultToolTip;
19 import org.eclipse.swt.custom.StyledText;
20 import org.eclipse.swt.dnd.DND;
21 import org.eclipse.swt.dnd.DragSource;
22 import org.eclipse.swt.dnd.Transfer;
23 import org.eclipse.swt.events.ControlAdapter;
24 import org.eclipse.swt.events.ControlEvent;
25 import org.eclipse.swt.events.ControlListener;
26 import org.eclipse.swt.events.FocusAdapter;
27 import org.eclipse.swt.events.FocusEvent;
28 import org.eclipse.swt.events.FocusListener;
29 import org.eclipse.swt.events.ModifyEvent;
30 import org.eclipse.swt.events.ModifyListener;
31 import org.eclipse.swt.events.MouseAdapter;
32 import org.eclipse.swt.events.MouseEvent;
33 import org.eclipse.swt.graphics.Color;
34 import org.eclipse.swt.graphics.Font;
35 import org.eclipse.swt.graphics.Image;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Control;
38 import org.eclipse.swt.widgets.Display;
39 import org.eclipse.swt.widgets.Label;
40 import org.eclipse.swt.widgets.Menu;
41 import org.eclipse.ui.forms.IFormPart;
42 import org.eclipse.ui.forms.IManagedForm;
43 import org.eclipse.ui.forms.widgets.TableWrapData;
44 import org.eclipse.ui.forms.widgets.TableWrapLayout;
45
46 import eu.etaxonomy.cdm.common.CdmUtils;
47 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
48 import eu.etaxonomy.cdm.model.common.CdmBase;
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.taxeditor.editor.CdmDataTransfer;
54 import eu.etaxonomy.taxeditor.editor.EditorUtil;
55 import eu.etaxonomy.taxeditor.editor.name.operation.CreateSynonymInNewGroupOperation;
56 import eu.etaxonomy.taxeditor.labels.ILabelImageStrategy;
57 import eu.etaxonomy.taxeditor.labels.LabelImageProvider;
58 import eu.etaxonomy.taxeditor.model.ImageResources;
59 import eu.etaxonomy.taxeditor.model.NameHelper;
60 import eu.etaxonomy.taxeditor.model.TextHelper;
61 import eu.etaxonomy.taxeditor.parser.ParseHandler;
62 import eu.etaxonomy.taxeditor.preference.Resources;
63
64 /**
65 * Formats <code>GroupedComposite</code> with cosmetic and layout properties specific to the
66 * Editor. This should be used to maintain a consistent look and feel for all Editor
67 * freetext area components, such as DescriptionElementComposite.
68 * <p>
69 * Requires an <code>IManagedForm</code>, whose <code>input</code> is set to the contents
70 * of {@link #getData()} when the <code>GroupedComposite</code> gets focus, i.e. to
71 * populate the property sheet with the data.
72 * </p>
73 * <p>
74 * The <code>IManagedForm</code> is also required to have a <code>Taxon</code> in its
75 * own <code>getData()</code>.
76 * </p>
77 * <p>
78 * The <code>IManagedForm</code> can also used for drawing borders by calling the method
79 * <code>createBorderSupport()</code>.
80 * </p>
81 *
82 * @author p.ciardelli
83 * @author n.hoffmann
84 * @created 02.06.2008
85 * @version 1.0
86 */
87 abstract public class AbstractGroupedContainer<T extends TaxonBase> implements IFormPart {
88
89 /**
90 *
91 */
92 public static final int SELECTED_FOCUS = 1;
93 /** Constant <code>SELECTED_NO_FOCUS=2</code> */
94 public static final int SELECTED_NO_FOCUS = 2;
95 /** Constant <code>NOT_SELECTED=3</code> */
96 public static final int NOT_SELECTED = 3;
97
98 /**
99 * ************ COMPOSITE TYPES ************
100 */
101 public String compositeType;
102 /** Constant <code>ACCEPTED_TAXON="accepted_name_composite"</code> */
103 public static final String ACCEPTED_TAXON = "accepted_name_composite";
104 /** Constant <code>HOMOTYPIC_SYNONYM="homotypic_name_composite"</code> */
105 public static final String HOMOTYPIC_SYNONYM = "homotypic_name_composite";
106 /** Constant <code>HETEROTYPIC_SYNONYM="heterotypic_name_composite"</code> */
107 public static final String HETEROTYPIC_SYNONYM = "heterotypic_name_composite";
108 /** Constant <code>MISAPPLIED_NAME="misappliedname_name_composite"</code> */
109 public static final String MISAPPLIED_NAME = "misappliedname_name_composite";
110 /** Constant <code>CONCEPTRELATION="concept_name_composite"</code> */
111 public static final String CONCEPTRELATION = "concept_name_composite";
112
113 /**
114 * ************ INDENTATIONS ************
115 */
116 public static final int ACCEPTED_INDENT = 20;
117 /** Constant <code>SYNONYM_INDENT=40</code> */
118 public static final int SYNONYM_INDENT = 40;
119 /** Constant <code>MISAPPLIEDNAME_INDENT=40</code> */
120 public static final int MISAPPLIEDNAME_INDENT = 40;
121 /** Constant <code>CONCEPT_INDENT=40</code> */
122 public static final int CONCEPT_INDENT = 40;
123
124
125 /**
126 * ************ ICONS ************
127 */
128 public static final Image ACCEPTED_ICON = ImageResources.getImage(ImageResources.BLACK_SQUARE_ICON);
129 /** Constant <code>HOMOTYPIC_SYNONYM_ICON</code> */
130 public static final Image HOMOTYPIC_SYNONYM_ICON = ImageResources.getImage(ImageResources.HOMOTYPIC_SYN_ICON);
131 /** Constant <code>HOMOTYPIC_SYNONYM_ORIGINAL_COMBINATION_ICON</code> */
132 public static final Image HOMOTYPIC_SYNONYM_ORIGINAL_COMBINATION_ICON = ImageResources.getImage(
133 ImageResources.HOMOTYPIC_SYN_ORIGINAL_ICON);
134 /** Constant <code>HETEROTYPIC_SYNONYM_ICON</code> */
135 public static final Image HETEROTYPIC_SYNONYM_ICON = ImageResources.getImage(ImageResources.HETEROTYPIC_SYN_ICON);
136 public static final Image INVALID_OR_NUDUM_NAME_ICON = ImageResources.getImage(ImageResources.NOM_INVAL_NUD_SYN_ICON);
137 /** Constant <code>HETEROTYPIC_SYNONYM_ORIGINAL_COMBINATION_ICON</code> */
138 public static final Image HETEROTYPIC_SYNONYM_ORIGINAL_COMBINATION_ICON = ImageResources.getImage(
139 ImageResources.HETEROTYPIC_SYN_ORIGINAL_ICON);
140 /** Constant <code>MISAPPLIEDNAME_ICON</code> */
141 public static final Image MISAPPLIEDNAME_ICON = ImageResources.getImage(ImageResources.MISAPPLIED_NAME_ICON);
142 /** Constant <code>CONCEPT_ICON</code> */
143 public static final Image CONCEPT_ICON = ImageResources.getImage(ImageResources.CONCEPT_ICON);
144 /** Constant <code>AUTONYM_ICON</code> */
145 public static final Image AUTONYM_ICON = ImageResources
146 .getImage(ImageResources.AUTONYM_ICON);
147 /** Constant <code>MOVE</code> */
148 public static final Image MOVE = ImageResources.getImage(ImageResources.MOVE_ICON);
149
150 /**
151 * ************ TRANSFORMATIONS ************
152 */
153 public static final String ADD_GROUP_BASIONYM = "add_group_basionym";
154 /** Constant <code>REMOVE_GROUP_BASIONYM="remove_group_basionym"</code> */
155 public static final String REMOVE_GROUP_BASIONYM = "remove_group_basionym";
156
157 private static final String EMPTY_NAME_PROMPT = "Click to add name";
158
159 protected ParseHandler parseHandler;
160
161 private FocusListener nameCompositeFocusListener;
162 private ModifyListener nameCompositeModifyListener;
163
164
165 protected TaxonNameEditor editor;
166
167 protected NameViewer nameViewer;
168
169 private AbstractGroup<T> group;
170
171 private Label nonEditableInfoLabel;
172 private DefaultToolTip nonEditableInfoHover;
173
174 private static AbstractGroupedContainer selection;
175
176 private FocusListener focusListener;
177 private LineBreakListener lineBreakListener;
178
179 private int cursorPosition;
180
181 protected Composite control;
182
183 private Color backgroundColor;
184 private boolean isDirty;
185
186
187 /**
188 * <p>Constructor for AbstractGroupedContainer.</p>
189 *
190 * @param editor a {@link eu.etaxonomy.taxeditor.editor.name.TaxonNameEditor} object.
191 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.AbstractGroup} object.
192 * @param taxonBase a T object.
193 * @param <T> a T object.
194 */
195 public AbstractGroupedContainer(TaxonNameEditor editor, AbstractGroup<T> group, T taxonBase){
196 this.editor = editor;
197
198 this.group = group;
199 group.add(this);
200
201 setData(taxonBase);
202
203 createControl();
204
205 createTextViewer();
206 createLineWrapSupport();
207 createLineBreakListener();
208
209 setMenu(editor.getMenu());
210
211 setDraggableControl(new Control[]{getControl(), getNameViewer().getRulerControl()});
212
213 createEmptyViewerPrompt(EMPTY_NAME_PROMPT);
214
215 parseHandler = ParseHandler.NewInstance(getNameViewer().getTextWidget(), taxonBase.getName());
216
217 initializeComposite();
218
219 createListener();
220
221 isFreeTextEnabled();
222 }
223
224
225
226 /**
227 * <p>createListener</p>
228 */
229 protected void createListener(){
230 nameCompositeModifyListener = new ModifyListener(){
231
232 public void modifyText(ModifyEvent e) {
233 // mark the composite dirty
234 setDirty(true);
235 // parse the text
236 String text = nameViewer.getTextWidget().getText();
237
238 NonViralName name = parseHandler.parse(text);
239 getTaxonBase().setName(name);
240 getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
241
242 // show errors resulting from parsing
243 calculateErrors();
244 // store the position of the cursor
245 storeCursor();
246 // notify selection listener
247 setDelayedSelection();
248 }
249 };
250 nameCompositeFocusListener = new FocusAdapter(){
251
252 /* (non-Javadoc)
253 * @see org.eclipse.swt.events.FocusAdapter#focusLost(org.eclipse.swt.events.FocusEvent)
254 */
255 @Override
256 public void focusLost(FocusEvent e) {
257 super.focusLost(e);
258
259 persistName();
260 }
261 };
262
263 addListener();
264 }
265
266 private void addListener(){
267 getNameViewer().getTextWidget().addModifyListener(nameCompositeModifyListener);
268 getNameViewer().getTextWidget().addFocusListener(nameCompositeFocusListener);
269 }
270
271 private void removeListener(){
272 getNameViewer().getTextWidget().removeModifyListener(nameCompositeModifyListener);
273 getNameViewer().getTextWidget().removeFocusListener(nameCompositeFocusListener);
274 }
275
276 /**
277 * Initialize the composite specific code
278 */
279 protected abstract void initializeComposite();
280
281 /**
282 * <p>getEmptyTextPrompt</p>
283 *
284 * @return a {@link java.lang.String} object.
285 */
286 protected String getEmptyTextPrompt() {
287 return EMPTY_NAME_PROMPT;
288 }
289
290 /**
291 *
292 */
293 private void showNameRelations() {
294 TaxonNameBase<?, ?> name = getName();
295 if (name == null) {
296 return;
297 }
298
299 ILabelImageStrategy strategy = LabelImageProvider.getLabelStrategy(name);
300 LabelImageProvider labelProvider = new LabelImageProvider(strategy);
301
302 Set<NameRelationship> nameRelations = name.getNameRelations();
303 if (nameRelations.size() == 0) {
304 return;
305 }
306 // for (NameRelationship nameRelation : nameRelations) {
307 // String typeLabel = null;
308 // TaxonNameBase<?, ?> relatedName = null;
309 //
310 // if (name.equals(nameRelation.getFromName())) {
311 // typeLabel = labelProvider.getNameRelationTypeLabel(
312 // nameRelation.getType());
313 // relatedName = nameRelation.getToName();
314 // } else {
315 // typeLabel = labelProvider.getNameRelationTypeInverseLabel(
316 // nameRelation.getType());
317 // relatedName = nameRelation.getFromName();
318 // }
319 //
320 // setNonEditableInfo(typeLabel + " " + NameHelper.getDisplayName(relatedName));
321 // }
322 }
323
324 /**
325 * <p>initTextViewer</p>
326 */
327 protected void initTextViewer() {
328
329 // showNameRelations();
330
331 updateIcon();
332
333 String text = NameHelper.getDisplayNameWithRef(getData());
334
335 if (text.length() == 0) {
336 initEmptyText();
337 } else {
338 getNameViewer().setText(text);
339 placeCursor();
340 }
341 calculateErrors();
342 }
343
344 /**
345 * <p>calculateErrors</p>
346 */
347 synchronized protected void calculateErrors() {
348 getNameViewer().clearErrors();
349 getNameViewer().setShowParsingErrors(getName());
350 }
351
352 /**
353 * <p>handleSplitText</p>
354 *
355 * @param text a {@link java.lang.String} object.
356 */
357 protected void handleSplitText(String text) {
358 // Create a synonym in a new homotypic group using text as name
359 TaxonNameBase synonymName = ParseHandler.quickParse(text);
360
361 EditorUtil.executeOperation(new CreateSynonymInNewGroupOperation
362 ("New Heterotypic Synonym", editor.getUndoContext(), editor.getTaxon(), synonymName, editor));
363 }
364
365 /**
366 * Refreshes the display with latest data from the model.
367 *
368 * Note: Will not parse the text and not calculate errors!
369 */
370 public void refresh() {
371 // showNameRelations();
372
373 String text = NameHelper.getDisplayNameWithRef(getTaxonBase());
374
375 if (text.length() == 0) {
376 initEmptyText();
377 } else {
378 removeListener();
379 getNameViewer().getTextWidget().setText(text);
380 addListener();
381 }
382
383 updateNonEditableInfo();
384
385 updateIcon();
386 // placeCursor();
387
388 isFreeTextEnabled();
389 }
390
391 /**
392 *
393 */
394 protected abstract void updateIcon();
395
396
397
398 /**
399 * <p>updateNonEditableInfo</p>
400 */
401 protected abstract void updateNonEditableInfo();
402
403 /**
404 *
405 */
406 private void isFreeTextEnabled() {
407 // Decide whether editing of freetext is allowed or not.
408 if (freetextEditingAllowed()) {
409
410 // set editable
411 getNameViewer().getTextWidget().setEditable(true);
412
413 // restore foreground font color again
414 getNameViewer().getTextWidget().setForeground(control.getForeground());
415 } else {
416 // set non-editable
417 getNameViewer().getTextWidget().setEditable(false);
418
419 // grey out text as a non-editable indicator
420 getNameViewer().getTextWidget().setForeground(EditorUtil.getColor(Resources.COLOR_DISABLED_EDITOR));
421 }
422 }
423
424
425 /**
426 * Checks whether the freetext should be editable based on specific empty fields.
427 * @return
428 */
429 private boolean freetextEditingAllowed() {
430 NonViralName name = (NonViralName) HibernateProxyHelper.deproxy(getName());
431 boolean enableFreetext = true;
432
433 enableFreetext &= isNameUsedMultipleTimes(name);
434 enableFreetext &= isNameParsable(name);
435
436 if(!enableFreetext){
437 getNameViewer().setShowNameNotParsableWarning(name);
438 }
439
440 return enableFreetext;
441 }
442
443 private boolean isNameUsedMultipleTimes(NonViralName name){
444 if(name.getTaxonBases().size() != 1){
445 getNameViewer().setShowMultipleNameUsageWarning(name);
446 return false;
447 }
448 return true;
449 }
450
451 private boolean isNameParsable(NonViralName name){
452 boolean isParsable = true;
453 isParsable &= CdmUtils.isEmpty(name.getAppendedPhrase()); //taxonFieldsEmpty();
454
455 isParsable &= ! name.isProtectedAuthorshipCache();
456 isParsable &= ! name.isProtectedNameCache();
457
458
459 return isParsable;
460 }
461
462 /**
463 * Parse the text and calculate errors
464 */
465 public void parseAndCalculateErrors(){
466 removeListener();
467 String unparsedNameString = getNameViewer().getTextWidget().getText();
468 parseHandler.parse(unparsedNameString);
469 addListener();
470 calculateErrors();
471 }
472
473 /**
474 * <p>getTaxonBase</p>
475 *
476 * @return the taxonBase
477 */
478 public T getTaxonBase() {
479 return getData();
480 }
481
482 /**
483 * <p>getName</p>
484 *
485 * @return a {@link eu.etaxonomy.cdm.model.name.TaxonNameBase} object.
486 */
487 public TaxonNameBase getName(){
488 return (TaxonNameBase) HibernateProxyHelper.deproxy(getTaxonBase().getName());
489 }
490
491 /**
492 * <p>persistName</p>
493 */
494 public void persistName(){
495 if(isDirty()){
496 getNameViewer().getTextWidget().setEnabled(false);
497 final String unparsedNameString = getNameViewer().getTextWidget().getText();
498 // Job job = new Job("Persisting Name"){
499 //
500 // @Override
501 // protected IStatus run(IProgressMonitor monitor) {
502 //
503 final NonViralName name = parseHandler.parseAndResolveDuplicates(unparsedNameString);
504 //
505 // Display.getDefault().asyncExec(new Runnable(){
506 // public void run() {
507 getTaxonBase().setName(name);
508 getTaxonBase().setTitleCache((getTaxonBase().generateTitle()));
509 setDirty(false);
510 getNameViewer().getTextWidget().setEnabled(true);
511 // };
512 // });
513 //
514 //
515 // return Status.OK_STATUS;
516 // }
517 //
518 // };
519 //
520 // job.setPriority(Job.DECORATE);
521 // job.schedule();
522 }
523 }
524
525 /**
526 * <p>Getter for the field <code>group</code>.</p>
527 *
528 * @return a {@link eu.etaxonomy.taxeditor.editor.name.AbstractGroup} object.
529 */
530 public AbstractGroup getGroup(){
531 return group;
532 }
533
534 /**
535 * <p>remove</p>
536 */
537 public void remove(){
538 getGroup().remove(this);
539 }
540
541 /**
542 * <p>createControl</p>
543 */
544 protected void createControl() {
545 control = editor.getToolkit().createComposite(group.getControl());
546
547 control.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
548 TableWrapLayout layout = new TableWrapLayout();
549 layout.leftMargin = 0;
550 layout.rightMargin = 0;
551 layout.topMargin = 5;
552 layout.bottomMargin = 5;
553
554 layout.verticalSpacing = 0;
555 layout.horizontalSpacing = 0;
556
557 control.setLayout(layout);
558
559 }
560
561 /**
562 * <p>Getter for the field <code>control</code>.</p>
563 *
564 * @return a {@link org.eclipse.swt.widgets.Composite} object.
565 */
566 public Composite getControl(){
567 return control;
568 }
569
570 /**
571 * <p>createLineWrapSupport</p>
572 */
573 protected void createLineWrapSupport() {
574 new LineWrapSupport(getNameViewer(), editor.getManagedForm());
575 }
576
577
578 /**
579 * <p>createTextViewer</p>
580 */
581 protected void createTextViewer() {
582 nameViewer = new NameViewer(control);
583
584 focusListener = new FocusAdapter() {
585 public void focusGained(FocusEvent e) {
586 for(AbstractGroupedContainer container : editor.getGroupedContainers()){
587 container.colorSelected(NOT_SELECTED);
588 }
589 editor.getManagedForm().setInput(AbstractGroupedContainer.this);
590 placeCursor();
591 colorSelected(SELECTED_FOCUS);
592 }
593 };
594 nameViewer.getTextWidget().addFocusListener(focusListener);
595
596 //
597 MouseAdapter mouseListener = new MouseAdapter() {
598 public void mouseDown(MouseEvent e) {
599 storeCursor();
600 }
601 };
602 control.addMouseListener(mouseListener);
603 nameViewer.getRulerControl().addMouseListener(mouseListener);
604 nameViewer.getTextWidget().addMouseListener(mouseListener);
605 }
606
607 /**
608 * <p>setIcon</p>
609 *
610 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
611 */
612 public void setIcon(Image icon) {
613 getNameViewer().setIcon(icon);
614 }
615
616 /**
617 * <p>setIndent</p>
618 *
619 * @param indent a int.
620 */
621 public void setIndent(int indent) {
622 if (control.getLayout() instanceof TableWrapLayout) {
623 TableWrapLayout layout = ((TableWrapLayout) control.getLayout());
624 layout.leftMargin = indent;
625 layout.rightMargin = ACCEPTED_INDENT;
626 control.setLayout(layout);
627 } else {
628 new RuntimeException("Couldn't indent - composite's layout must be TableWrapLayout.");
629 }
630 }
631
632 /**
633 * <p>setSelected</p>
634 */
635 public void setSelected() {
636 getNameViewer().getTextWidget().setFocus();
637 }
638
639 /**
640 * <p>isSelected</p>
641 *
642 * @return a boolean.
643 */
644 public boolean isSelected(){
645 return editor.getSelectedObject() == this;
646 }
647
648 /**
649 * <p>colorSelected</p>
650 *
651 * @param mode a int.
652 */
653 public void colorSelected(int mode){
654 if(!control.isDisposed()){
655 String colorString = null;
656
657 switch(mode){
658 case SELECTED_FOCUS:
659 colorString = Resources.COLOR_CONTROL_SELECTED_FOCUS;
660 break;
661 case SELECTED_NO_FOCUS:
662 colorString = Resources.COLOR_CONTROL_SELECTED;
663 break;
664 default:
665 colorString = Resources.COLOR_COMPOSITE_BACKGROUND;
666 }
667
668 backgroundColor = EditorUtil.getColor(colorString);
669
670 setBackground(backgroundColor);
671 }
672 }
673
674
675 /**
676 * <p>setDelayedSelection</p>
677 */
678 protected void setDelayedSelection(){
679 //TODO this might be done better
680 // this is the quickest solution i could come up with and it improves performance
681 // please reimplement if you know better.
682 selection = this;
683
684 // start timer
685 Display display = Display.getCurrent();
686 Runnable runnable = new Runnable() {
687
688 public void run() {
689 editor.getManagedForm().setInput(selection);
690 }
691 };
692 display.timerExec(1000, runnable);
693
694 }
695
696 /**
697 * <p>setBackground</p>
698 *
699 * @param color a {@link org.eclipse.swt.graphics.Color} object.
700 */
701 public void setBackground(Color color) {
702 control.setBackground(color);
703
704 for(Control child : control.getChildren()){
705 child.setBackground(color);
706 }
707
708 getNameViewer().setBackground(color);
709 }
710
711 /* (non-Javadoc)
712 * @see org.eclipse.swt.widgets.Control#setFont(org.eclipse.swt.graphics.Font)
713 */
714 /**
715 * <p>setFont</p>
716 *
717 * @param font a {@link org.eclipse.swt.graphics.Font} object.
718 */
719 public void setFont(Font font) {
720 getNameViewer().getTextWidget().setFont(font);
721 }
722
723 /**
724 * <p>Getter for the field <code>nameViewer</code>.</p>
725 *
726 * @return a {@link eu.etaxonomy.taxeditor.editor.name.NameViewer} object.
727 */
728 public NameViewer getNameViewer() {
729 if (nameViewer == null){
730 throw new RuntimeException("The Name Viewer is corrupt for Name Container: " + getTaxonBase().getName().getTitleCache());
731 }
732 return nameViewer;
733 }
734
735 /**
736 * If <code>textViewer</code> has already been set, it will show a
737 * <code>prompt</code> along the lines of "Click here to start entering data"
738 * when empty.
739 *
740 * @param prompt a {@link java.lang.String} object.
741 */
742 public void createEmptyViewerPrompt(final String prompt) {
743
744 Assert.isNotNull(getNameViewer());
745
746 final StyledText textControl = getNameViewer().getTextWidget();
747 final IDocument document = getNameViewer().getDocument();
748
749 setFocusListener(new FocusListener() {
750
751
752 public void focusGained(FocusEvent e) {
753 if (document.get().equals(prompt)) {
754 textControl.setFont(getViewerFont());
755 document.set("");
756 }
757 }
758
759
760 public void focusLost(FocusEvent e) {
761 if (document.getLength() == 0) {
762 initEmptyText();
763 }
764 }
765
766 });
767 textControl.addFocusListener(getFocusListener());
768
769 if (document.getLength() == 0) {
770 textControl.setFont(EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT));
771 document.set(prompt);
772 }
773 }
774
775 /**
776 * <p>getViewerFont</p>
777 *
778 * @return a {@link org.eclipse.swt.graphics.Font} object.
779 */
780 abstract protected Font getViewerFont();
781
782 /**
783 * <p>initEmptyText</p>
784 */
785 protected void initEmptyText() {
786 Font defaultFont = EditorUtil.getFont(Resources.FONT_DEFAULT_PROMPT);
787 getNameViewer().getTextWidget().setFont(defaultFont);
788
789 getNameViewer().getDocument().set(getEmptyTextPrompt());
790 placeCursor();
791 }
792
793 /**
794 * <p>Setter for the field <code>focusListener</code>.</p>
795 *
796 * @param focusListener a {@link org.eclipse.swt.events.FocusListener} object.
797 */
798 protected void setFocusListener(FocusListener focusListener) {
799 this.focusListener = focusListener;
800 }
801
802 private FocusListener getFocusListener() {
803 return focusListener;
804 }
805
806 /**
807 * <p>setDirty</p>
808 *
809 * @param isDirty a boolean.
810 */
811 public void setDirty(boolean isDirty) {
812 if(isDirty){
813 editor.getManagedForm().dirtyStateChanged();
814 }
815 this.isDirty = isDirty;
816 }
817
818 /**
819 * <p>isDirty</p>
820 *
821 * @return a boolean.
822 */
823 public boolean isDirty(){
824 return isDirty;
825 }
826
827 /**
828 * <p>setMenu</p>
829 *
830 * @param menu a {@link org.eclipse.swt.widgets.Menu} object.
831 */
832 public void setMenu (Menu menu) {
833 control.setMenu(menu);
834
835 getNameViewer().setMenu(menu);
836 }
837
838 private Control[] draggableControls;
839
840 /**
841 * <p>setDraggableControl</p>
842 *
843 * @param controls an array of {@link org.eclipse.swt.widgets.Control} objects.
844 */
845 protected void setDraggableControl(Control[] controls) {
846 draggableControls = controls;
847 }
848
849 /**
850 * <p>setIsDraggable</p>
851 *
852 * @param draggable a boolean.
853 */
854 public void setIsDraggable(boolean draggable) {
855
856 if (draggable) {
857
858 if (draggableControls == null) {
859 throw new NullPointerException(
860 "Draggable controls must be set to add draggability");
861 }
862
863 Transfer[] types = new Transfer[] { CdmDataTransfer.getInstance() };
864 int operations = DND.DROP_MOVE;
865
866 for(Control draggableControl : draggableControls){
867 DragSource dragSource = new DragSource(draggableControl, operations);
868 dragSource.setTransfer(types);
869
870 dragSource.addDragListener(new NameEditorDragListener(this));
871 dragSource.setDragSourceEffect(new NameEditorDragSourceEffect(control));
872 }
873 }
874 }
875
876 private String nonEditableText;
877
878 ControlListener nonEditableResizeListener = new ControlAdapter() {
879
880 int width = 0;
881
882 public void controlResized(ControlEvent e) {
883 if (nonEditableInfoLabel.getBounds().width == width) {
884 return;
885 }
886 width = nonEditableInfoLabel.getBounds().width;
887 if (nonEditableInfoLabel.getBounds().width > 0) {
888 nonEditableInfoLabel.setText(
889 Dialog.shortenText(nonEditableText.toUpperCase(),
890 nonEditableInfoLabel));
891 }
892 }
893 };
894
895 private String nonEditableHoverText;
896
897 private LabelEllipsisListener nonEditableLabelEllipsisListener;
898
899 private T data;
900
901 /**
902 * nonEditableInfo is a label displayed underneath a GroupedComposite's
903 * input field. For instance, NameComposites display things like name relations,
904 * sec. references, etc. here.
905 *
906 * @param info the text to display in the label
907 * @param append whether the string should be appended to text that is already shown in the label
908 */
909 public void setNonEditableInfo(String info, boolean append) {
910 // TODO non editable info should only be drawn once, when everything else is drawn
911 info = info.toUpperCase();
912
913 if(append){
914 nonEditableText += ", " + info;
915 nonEditableHoverText += "\n" + info;
916 }else{
917 nonEditableText = info;
918 nonEditableHoverText = info;
919 }
920
921 if (nonEditableInfoLabel == null) {
922 nonEditableInfoLabel = editor.getToolkit().createLabel(control, "");
923 TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP);
924 // Set indent to viewer ruler's width
925 if (getNameViewer().getRulerControl() != null) {
926 // TODO right justify
927 layoutData.indent = NameViewer.RULER_WIDTH;
928 }
929 nonEditableInfoLabel.setLayoutData(layoutData);
930
931
932
933 nonEditableLabelEllipsisListener = new LabelEllipsisListener(nonEditableInfoLabel) {
934 @Override
935 public String getLabelText() {
936 return nonEditableText.toUpperCase();
937 }
938 };
939 nonEditableInfoLabel.addControlListener(nonEditableLabelEllipsisListener);
940
941 nonEditableInfoHover = new DefaultToolTip(nonEditableInfoLabel);
942 nonEditableInfoHover.setRespectDisplayBounds(true);
943
944 }
945 nonEditableInfoHover.setText(nonEditableHoverText);
946 nonEditableInfoLabel.setText(nonEditableText);
947 }
948
949 /**
950 * <p>Getter for the field <code>data</code>.</p>
951 *
952 * @return a T object.
953 */
954 public T getData(){
955 return data;
956 }
957
958 /**
959 * <p>Setter for the field <code>data</code>.</p>
960 *
961 * @param data a T object.
962 */
963 public void setData(T data){
964 this.data = (T) HibernateProxyHelper.deproxy(data);
965 }
966
967 /**
968 * If the user hitting carriage return should cause something to happen -
969 * i.e. the creation of a new composite - call this method and override
970 * the method handleSplitText().
971 */
972 protected void createLineBreakListener() {
973 lineBreakListener = new LineBreakListener() {
974 @Override
975 public void handleSplitText(String text) {
976 AbstractGroupedContainer.this.handleSplitText(text);
977 }
978 };
979
980 getNameViewer().getTextWidget().addVerifyListener(lineBreakListener);
981 getNameViewer().getTextWidget().addKeyListener(lineBreakListener);
982 }
983
984 abstract class LabelEllipsisListener extends ControlAdapter {
985
986 private Label label;
987 int width = 0;
988
989 LabelEllipsisListener(Label label) {
990 this.label = label;
991 }
992
993 abstract public String getLabelText();
994
995 public void controlResized(ControlEvent e) {
996 if (label.getBounds().width == width) {
997 return;
998 }
999 width = label.getBounds().width;
1000 if (label.getBounds().width > 0) {
1001 label.setText(TextHelper.shortenText(getLabelText(), label));
1002 }
1003 }
1004 }
1005
1006 /**
1007 * <p>storeCursor</p>
1008 */
1009 public void storeCursor() {
1010 this.cursorPosition = getNameViewer().getCursorPosition();
1011 }
1012
1013 /**
1014 * Puts the cursor to the position it was last seen on or to the end of line
1015 * if no former position is known.
1016 */
1017 public void placeCursor(){
1018 if(cursorPosition == 0){
1019 getNameViewer().setCursorToEOL();
1020 }else{
1021 getNameViewer().setCursorPosition(cursorPosition);
1022 }
1023 }
1024
1025
1026
1027 /**
1028 * <p>Setter for the field <code>group</code>.</p>
1029 *
1030 * @param group a {@link eu.etaxonomy.taxeditor.editor.name.AbstractGroup} object.
1031 */
1032 public void setGroup(AbstractGroup<T> group) {
1033 this.group = group;
1034 }
1035
1036
1037
1038 /**
1039 * <p>restoreColor</p>
1040 */
1041 public void restoreColor() {
1042 setBackground(backgroundColor);
1043 }
1044
1045
1046
1047 /* (non-Javadoc)
1048 * @see org.eclipse.ui.forms.IFormPart#initialize(org.eclipse.ui.forms.IManagedForm)
1049 */
1050 @Override
1051 public void initialize(IManagedForm form) {
1052 // TODO Auto-generated method stub
1053
1054 }
1055
1056
1057 /* (non-Javadoc)
1058 * @see org.eclipse.ui.forms.IFormPart#dispose()
1059 */
1060 @Override
1061 public void dispose() {
1062 // TODO Auto-generated method stub
1063
1064 }
1065
1066
1067 /* (non-Javadoc)
1068 * @see org.eclipse.ui.forms.IFormPart#commit(boolean)
1069 */
1070 @Override
1071 public void commit(boolean onSave) {
1072 if(isDirty()){
1073 persistName();
1074 }
1075 }
1076
1077
1078 /* (non-Javadoc)
1079 * @see org.eclipse.ui.forms.IFormPart#setFormInput(java.lang.Object)
1080 */
1081 @Override
1082 public boolean setFormInput(Object input) {
1083 // TODO Auto-generated method stub
1084 return false;
1085 }
1086
1087
1088 /* (non-Javadoc)
1089 * @see org.eclipse.ui.forms.IFormPart#setFocus()
1090 */
1091 @Override
1092 public void setFocus() {
1093 getNameViewer().getControl().setFocus();
1094 }
1095
1096
1097 /* (non-Javadoc)
1098 * @see org.eclipse.ui.forms.IFormPart#isStale()
1099 */
1100 @Override
1101 public boolean isStale() {
1102 // TODO Auto-generated method stub
1103 return false;
1104 }
1105 }