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