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